* Panic don't fatal on create new logger Fixes #5854 Signed-off-by: Andrew Thornton <art27@cantab.net> * partial broken * Update the logging infrastrcture Signed-off-by: Andrew Thornton <art27@cantab.net> * Reset the skip levels for Fatal and Error Signed-off-by: Andrew Thornton <art27@cantab.net> * broken ncsa * More log.Error fixes Signed-off-by: Andrew Thornton <art27@cantab.net> * Remove nal * set log-levels to lowercase * Make console_test test all levels * switch to lowercased levels * OK now working * Fix vetting issues * Fix lint * Fix tests * change default logging to match current gitea * Improve log testing Signed-off-by: Andrew Thornton <art27@cantab.net> * reset error skip levels to 0 * Update documentation and access logger configuration * Redirect the router log back to gitea if redirect macaron log but also allow setting the log level - i.e. TRACE * Fix broken level caching * Refactor the router log * Add Router logger * Add colorizing options * Adjust router colors * Only create logger if they will be used * update app.ini.sample * rename Attribute ColorAttribute * Change from white to green for function * Set fatal/error levels * Restore initial trace logger * Fix Trace arguments in modules/auth/auth.go * Properly handle XORMLogger * Improve admin/config page * fix fmt * Add auto-compression of old logs * Update error log levels * Remove the unnecessary skip argument from Error, Fatal and Critical * Add stacktrace support * Fix tests * Remove x/sync from vendors? * Add stderr option to console logger * Use filepath.ToSlash to protect against Windows in tests * Remove prefixed underscores from names in colors.go * Remove not implemented database logger This was removed from Gogs on 4 Mar 2016 but left in the configuration since then. * Ensure that log paths are relative to ROOT_PATH * use path.Join * rename jsonConfig to logConfig * Rename "config" to "jsonConfig" to make it clearer * Requested changes * Requested changes: XormLogger * Try to color the windows terminal If successful default to colorizing the console logs * fixup * Colorize initially too * update vendor * Colorize logs on default and remove if this is not a colorizing logger * Fix documentation * fix test * Use go-isatty to detect if on windows we are on msys or cygwin * Fix spelling mistake * Add missing vendors * More changes * Rationalise the ANSI writer protection * Adjust colors on advice from @0x5c * Make Flags a comma separated list * Move to use the windows constant for ENABLE_VIRTUAL_TERMINAL_PROCESSING * Ensure matching is done on the non-colored message - to simpify EXPRESSION
This commit is contained in:
		
							parent
							
								
									ef2a343e27
								
							
						
					
					
						commit
						704da08fdc
					
				
					 301 changed files with 36993 additions and 8244 deletions
				
			
		|  | @ -204,14 +204,14 @@ func runHookPostReceive(c *cli.Context) error { | |||
| 			RepoUserName: repoUser, | ||||
| 			RepoName:     repoName, | ||||
| 		}); err != nil { | ||||
| 			log.GitLogger.Error(2, "Update: %v", err) | ||||
| 			log.GitLogger.Error("Update: %v", err) | ||||
| 		} | ||||
| 
 | ||||
| 		if newCommitID != git.EmptySHA && strings.HasPrefix(refFullName, git.BranchPrefix) { | ||||
| 			branch := strings.TrimPrefix(refFullName, git.BranchPrefix) | ||||
| 			repo, pullRequestAllowed, err := private.GetRepository(repoID) | ||||
| 			if err != nil { | ||||
| 				log.GitLogger.Error(2, "get repo: %v", err) | ||||
| 				log.GitLogger.Error("get repo: %v", err) | ||||
| 				break | ||||
| 			} | ||||
| 			if !pullRequestAllowed { | ||||
|  | @ -229,7 +229,7 @@ func runHookPostReceive(c *cli.Context) error { | |||
| 
 | ||||
| 			pr, err := private.ActivePullRequest(baseRepo.ID, repo.ID, baseRepo.DefaultBranch, branch) | ||||
| 			if err != nil { | ||||
| 				log.GitLogger.Error(2, "get active pr: %v", err) | ||||
| 				log.GitLogger.Error("get active pr: %v", err) | ||||
| 				break | ||||
| 			} | ||||
| 
 | ||||
|  |  | |||
|  | @ -44,7 +44,7 @@ func runMigrate(ctx *cli.Context) error { | |||
| 	models.LoadConfigs() | ||||
| 
 | ||||
| 	if err := models.NewEngine(migrations.Migrate); err != nil { | ||||
| 		log.Fatal(4, "Failed to initialize ORM engine: %v", err) | ||||
| 		log.Fatal("Failed to initialize ORM engine: %v", err) | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
|  |  | |||
|  | @ -100,7 +100,7 @@ func fail(userMessage, logMessage string, args ...interface{}) { | |||
| 		if !setting.ProdMode { | ||||
| 			fmt.Fprintf(os.Stderr, logMessage+"\n", args...) | ||||
| 		} | ||||
| 		log.GitLogger.Fatal(3, logMessage, args...) | ||||
| 		log.GitLogger.Fatal(logMessage, args...) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										14
									
								
								cmd/web.go
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								cmd/web.go
									
									
									
									
									
								
							|  | @ -69,7 +69,7 @@ func runHTTPRedirector() { | |||
| 	var err = runHTTP(source, context2.ClearHandler(handler)) | ||||
| 
 | ||||
| 	if err != nil { | ||||
| 		log.Fatal(4, "Failed to start port redirection: %v", err) | ||||
| 		log.Fatal("Failed to start port redirection: %v", err) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | @ -84,7 +84,7 @@ func runLetsEncrypt(listenAddr, domain, directory, email string, m http.Handler) | |||
| 		log.Info("Running Let's Encrypt handler on %s", setting.HTTPAddr+":"+setting.PortToRedirect) | ||||
| 		var err = http.ListenAndServe(setting.HTTPAddr+":"+setting.PortToRedirect, certManager.HTTPHandler(http.HandlerFunc(runLetsEncryptFallbackHandler))) // all traffic coming into HTTP will be redirect to HTTPS automatically (LE HTTP-01 validation happens here)
 | ||||
| 		if err != nil { | ||||
| 			log.Fatal(4, "Failed to start the Let's Encrypt handler on port %s: %v", setting.PortToRedirect, err) | ||||
| 			log.Fatal("Failed to start the Let's Encrypt handler on port %s: %v", setting.PortToRedirect, err) | ||||
| 		} | ||||
| 	}() | ||||
| 	server := &http.Server{ | ||||
|  | @ -192,13 +192,13 @@ func runWeb(ctx *cli.Context) error { | |||
| 	case setting.FCGI: | ||||
| 		listener, err := net.Listen("tcp", listenAddr) | ||||
| 		if err != nil { | ||||
| 			log.Fatal(4, "Failed to bind %s", listenAddr, err) | ||||
| 			log.Fatal("Failed to bind %s: %v", listenAddr, err) | ||||
| 		} | ||||
| 		defer listener.Close() | ||||
| 		err = fcgi.Serve(listener, context2.ClearHandler(m)) | ||||
| 	case setting.UnixSocket: | ||||
| 		if err := os.Remove(listenAddr); err != nil && !os.IsNotExist(err) { | ||||
| 			log.Fatal(4, "Failed to remove unix socket directory %s: %v", listenAddr, err) | ||||
| 			log.Fatal("Failed to remove unix socket directory %s: %v", listenAddr, err) | ||||
| 		} | ||||
| 		var listener *net.UnixListener | ||||
| 		listener, err = net.ListenUnix("unix", &net.UnixAddr{Name: listenAddr, Net: "unix"}) | ||||
|  | @ -209,15 +209,15 @@ func runWeb(ctx *cli.Context) error { | |||
| 		// FIXME: add proper implementation of signal capture on all protocols
 | ||||
| 		// execute this on SIGTERM or SIGINT: listener.Close()
 | ||||
| 		if err = os.Chmod(listenAddr, os.FileMode(setting.UnixSocketPermission)); err != nil { | ||||
| 			log.Fatal(4, "Failed to set permission of unix socket: %v", err) | ||||
| 			log.Fatal("Failed to set permission of unix socket: %v", err) | ||||
| 		} | ||||
| 		err = http.Serve(listener, context2.ClearHandler(m)) | ||||
| 	default: | ||||
| 		log.Fatal(4, "Invalid protocol: %s", setting.Protocol) | ||||
| 		log.Fatal("Invalid protocol: %s", setting.Protocol) | ||||
| 	} | ||||
| 
 | ||||
| 	if err != nil { | ||||
| 		log.Fatal(4, "Failed to start server: %v", err) | ||||
| 		log.Fatal("Failed to start server: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
|  |  | |||
|  | @ -34,7 +34,7 @@ func runHTTPS(listenAddr, certFile, keyFile string, m http.Handler) error { | |||
| 	var err error | ||||
| 	config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile) | ||||
| 	if err != nil { | ||||
| 		log.Fatal(4, "Failed to load https cert file %s: %v", listenAddr, err) | ||||
| 		log.Fatal("Failed to load https cert file %s: %v", listenAddr, err) | ||||
| 	} | ||||
| 
 | ||||
| 	return gracehttp.Serve(&http.Server{ | ||||
|  |  | |||
|  | @ -1,3 +1,4 @@ | |||
| 
 | ||||
| ; This file lists the default values used by Gitea | ||||
| ; Copy required sections to your own app.ini (default is custom/conf/app.ini) | ||||
| ; and modify as needed. | ||||
|  | @ -33,7 +34,7 @@ PREFERRED_LICENSES = Apache License 2.0,MIT License | |||
| DISABLE_HTTP_GIT = false | ||||
| ; Value for Access-Control-Allow-Origin header, default is not to present | ||||
| ; WARNING: This maybe harmful to you website if you do not give it a right value. | ||||
| ACCESS_CONTROL_ALLOW_ORIGIN =  | ||||
| ACCESS_CONTROL_ALLOW_ORIGIN = | ||||
| ; Force ssh:// clone url instead of scp-style uri when default SSH port is used | ||||
| USE_COMPAT_SSH_URI = false | ||||
| ; Close issues as long as a commit on any branch marks it as fixed | ||||
|  | @ -260,7 +261,7 @@ ISSUE_INDEXER_TYPE = bleve | |||
| ISSUE_INDEXER_PATH = indexers/issues.bleve | ||||
| ; Issue indexer queue, currently support: channel or levelqueue, default is levelqueue | ||||
| ISSUE_INDEXER_QUEUE_TYPE = levelqueue | ||||
| ; When ISSUE_INDEXER_QUEUE_TYPE is levelqueue, this will be the queue will be saved path,  | ||||
| ; When ISSUE_INDEXER_QUEUE_TYPE is levelqueue, this will be the queue will be saved path, | ||||
| ; default is indexers/issues.queue | ||||
| ISSUE_INDEXER_QUEUE_DIR = indexers/issues.queue | ||||
| ; Batch queue number, default is 20 | ||||
|  | @ -390,8 +391,8 @@ NO_REPLY_ADDRESS = noreply.example.org | |||
| ; Show Registration button | ||||
| SHOW_REGISTRATION_BUTTON = true | ||||
| ; Default value for AutoWatchNewRepos | ||||
| ; When adding a repo to a team or creating a new repo all team members will watch the  | ||||
| ; repo automatically if enabled  | ||||
| ; When adding a repo to a team or creating a new repo all team members will watch the | ||||
| ; repo automatically if enabled | ||||
| AUTO_WATCH_NEW_REPOS = true | ||||
| 
 | ||||
| [webhook] | ||||
|  | @ -516,17 +517,37 @@ ROOT_PATH = | |||
| MODE = console | ||||
| ; Buffer length of the channel, keep it as it is if you don't know what it is. | ||||
| BUFFER_LEN = 10000 | ||||
| ; Either "Trace", "Debug", "Info", "Warn", "Error", "Critical", default is "Trace" | ||||
| LEVEL = Trace | ||||
| REDIRECT_MACARON_LOG = false | ||||
| MACARON = file | ||||
| ; Either "Trace", "Debug", "Info", "Warn", "Error", "Critical", default is "Info" | ||||
| ROUTER_LOG_LEVEL = Info | ||||
| ROUTER = console | ||||
| ENABLE_ACCESS_LOG = false | ||||
| ACCESS_LOG_TEMPLATE = {{.Ctx.RemoteAddr}} - {{.Identity}} {{.Start.Format "[02/Jan/2006:15:04:05 -0700]" }} "{{.Ctx.Req.Method}} {{.Ctx.Req.RequestURI}} {{.Ctx.Req.Proto}}" {{.ResponseWriter.Status}} {{.ResponseWriter.Size}} "{{.Ctx.Req.Referer}}\" \"{{.Ctx.Req.UserAgent}}" | ||||
| ACCESS = file | ||||
| ; Either "Trace", "Debug", "Info", "Warn", "Error", "Critical", default is "Trace" | ||||
| LEVEL = Info | ||||
| ; Either "Trace", "Debug", "Info", "Warn", "Error", "Critical", default is "None" | ||||
| STACKTRACE_LEVEL = None | ||||
| 
 | ||||
| ; Generic log modes | ||||
| [log.x] | ||||
| FLAGS = stdflags | ||||
| EXPRESSION = | ||||
| PREFIX = | ||||
| COLORIZE = false | ||||
| 
 | ||||
| ; For "console" mode only | ||||
| [log.console] | ||||
| LEVEL = | ||||
| STDERR = false | ||||
| 
 | ||||
| ; For "file" mode only | ||||
| [log.file] | ||||
| LEVEL = | ||||
| ; Set the file_name for the logger. If this is a relative path this | ||||
| ; will be relative to ROOT_PATH | ||||
| FILE_NAME = | ||||
| ; This enables automated log rotate(switch of following options), default is true | ||||
| LOG_ROTATE = true | ||||
| ; Max number of lines in a single file, default is 1000000 | ||||
|  | @ -537,6 +558,10 @@ MAX_SIZE_SHIFT = 28 | |||
| DAILY_ROTATE = true | ||||
| ; delete the log file after n days, default is 7 | ||||
| MAX_DAYS = 7 | ||||
| ; compress logs with gzip | ||||
| COMPRESS = true | ||||
| ; compression level see godoc for compress/gzip | ||||
| COMPRESSION_LEVEL = -1 | ||||
| 
 | ||||
| ; For "conn" mode only | ||||
| [log.conn] | ||||
|  | @ -564,14 +589,6 @@ PASSWD = | |||
| ; Receivers, can be one or more, e.g. 1@example.com,2@example.com | ||||
| RECEIVERS = | ||||
| 
 | ||||
| ; For "database" mode only | ||||
| [log.database] | ||||
| LEVEL = | ||||
| ; Either "mysql" or "postgres" | ||||
| DRIVER = | ||||
| ; Based on xorm, e.g.: root:root@localhost/gitea?charset=utf8 | ||||
| CONN = | ||||
| 
 | ||||
| [cron] | ||||
| ; Enable running cron tasks periodically. | ||||
| ENABLED = true | ||||
|  |  | |||
|  | @ -68,10 +68,12 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`. | |||
| - `DEFAULT_CLOSE_ISSUES_VIA_COMMITS_IN_ANY_BRANCH`:  **false**: Close an issue if a commit on a non default branch marks it as closed. | ||||
| 
 | ||||
| ### Repository - Pull Request (`repository.pull-request`) | ||||
| 
 | ||||
| - `WORK_IN_PROGRESS_PREFIXES`: **WIP:,\[WIP\]**: List of prefixes used in Pull Request | ||||
|  title to mark them as Work In Progress | ||||
| 
 | ||||
| ### Repository - Issue (`repository.issue`) | ||||
| 
 | ||||
| - `LOCK_REASONS`: **Too heated,Off-topic,Resolved,Spam**: A list of reasons why a Pull Request or Issue can be locked | ||||
| 
 | ||||
| ## UI (`ui`) | ||||
|  | @ -287,9 +289,65 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`. | |||
| ## Log (`log`) | ||||
| 
 | ||||
| - `ROOT_PATH`: **\<empty\>**: Root path for log files. | ||||
| - `MODE`: **console**: Logging mode. For multiple modes, use a comma to separate values. | ||||
| - `LEVEL`: **Trace**: General log level. \[Trace, Debug, Info, Warn, Error, Critical\] | ||||
| - `REDIRECT_MACARON_LOG`: **false**: Redirects the Macaron log to the Gitea logger. | ||||
| - `MODE`: **console**: Logging mode. For multiple modes, use a comma to separate values. You can configure each mode in per mode log subsections `\[log.modename\]`. By default the file mode will log to `$ROOT_PATH/gitea.log`. | ||||
| - `LEVEL`: **Info**: General log level. \[Trace, Debug, Info, Warn, Error, Critical, Fatal, None\] | ||||
| - `STACKTRACE_LEVEL`: **None**: Default log level at which to log create stack traces. \[Trace, Debug, Info, Warn, Error, Critical, Fatal, None\] | ||||
| - `REDIRECT_MACARON_LOG`: **false**: Redirects the Macaron log to its own logger or the default logger. | ||||
| - `MACARON`: **file**: Logging mode for the macaron logger, use a comma to separate values. Configure each mode in per mode log subsections `\[log.modename.macaron\]`. By default the file mode will log to `$ROOT_PATH/macaron.log`. (If you set this to `,` it will log to default gitea logger.) | ||||
| - `ROUTER_LOG_LEVEL`: **Info**: The log level that the router should log at. (If you are setting the access log, its recommended to place this at Debug.) | ||||
| - `ROUTER`: **console**: The mode or name of the log the router should log to. (If you set this to `,` it will log to default gitea logger.) | ||||
| NB: You must `REDIRECT_MACARON_LOG` and have `DISABLE_ROUTER_LOG` set to `false` for this option to take effect. Configure each mode in per mode log subsections `\[log.modename.router\]`. | ||||
| - `ENABLE_ACCESS_LOG`: **false**: Creates an access.log in NCSA common log format, or as per the following template | ||||
| - `ACCESS`: **file**: Logging mode for the access logger, use a comma to separate values. Configure each mode in per mode log subsections `\[log.modename.access\]`. By default the file mode will log to `$ROOT_PATH/access.log`. (If you set this to `,` it will log to the default gitea logger.) | ||||
| - `ACCESS_LOG_TEMPLATE`: **`{{.Ctx.RemoteAddr}} - {{.Identity}} {{.Start.Format "[02/Jan/2006:15:04:05 -0700]" }} "{{.Ctx.Req.Method}} {{.Ctx.Req.RequestURI}} {{.Ctx.Req.Proto}}" {{.ResponseWriter.Status}} {{.ResponseWriter.Size}} "{{.Ctx.Req.Referer}}\" \"{{.Ctx.Req.UserAgent}}"`**: Sets the template used to create the access log. | ||||
|   - The following variables are available: | ||||
|   - `Ctx`: the `macaron.Context` of the request. | ||||
|   - `Identity`: the SignedUserName or `"-"` if not logged in. | ||||
|   - `Start`: the start time of the request. | ||||
|   - `ResponseWriter`: the responseWriter from the request. | ||||
|   - You must be very careful to ensure that this template does not throw errors or panics as this template runs outside of the panic/recovery script. | ||||
| - `ENABLE_XORM_LOG`: **true**: Set whether to perform XORM logging. Please note SQL statement logging can be disabled by setting `LOG_SQL` to false in the `[database]` section. | ||||
| 
 | ||||
| ### Log subsections (`log.name`, `log.name.*`) | ||||
| 
 | ||||
| - `LEVEL`: **log.LEVEL**: Sets the log-level of this sublogger. Defaults to the `LEVEL` set in the global `[log]` section. | ||||
| - `STACKTRACE_LEVEL`: **log.STACKTRACE_LEVEL**: Sets the log level at which to log stack traces. | ||||
| - `MODE`: **name**: Sets the mode of this sublogger - Defaults to the provided subsection name. This allows you to have two different file loggers at different levels. | ||||
| - `EXPRESSION`: **""**: A regular expression to match either the function name, file or message. Defaults to empty. Only log messages that match the expression will be saved in the logger. | ||||
| - `FLAGS`: **stdflags**: A comma separated string representing the log flags. Defaults to `stdflags` which represents the prefix: `2009/01/23 01:23:23 ...a/b/c/d.go:23:runtime.Caller() [I]: message`. `none` means don't prefix log lines. See `modules/log/base.go` for more information. | ||||
| - `PREFIX`: **""**: An additional prefix for every log line in this logger. Defaults to empty. | ||||
| - `COLORIZE`: **false**: Colorize the log lines by default | ||||
| 
 | ||||
| ### Console log mode (`log.console`, `log.console.*`, or `MODE=console`) | ||||
| 
 | ||||
| - For the console logger `COLORIZE` will default to `true` if not on windows. | ||||
| - `STDERR`: **false**: Use Stderr instead of Stdout. | ||||
| 
 | ||||
| ### File log mode (`log.file`, `log.file.*` or `MODE=file`) | ||||
| 
 | ||||
| - `FILE_NAME`: Set the file name for this logger. Defaults as described above. If relative will be relative to the `ROOT_PATH` | ||||
| - `LOG_ROTATE`: **true**: Rotate the log files. | ||||
| - `MAX_SIZE_SHIFT`: **28**: Maximum size shift of a single file, 28 represents 256Mb. | ||||
| - `DAILY_ROTATE`: **true**: Rotate logs daily. | ||||
| - `MAX_DAYS`: **7**: Delete the log file after n days | ||||
| - NB: `COLORIZE`: will default to `true` if not on windows. | ||||
| - `COMPRESS`: **true**: Compress old log files by default with gzip | ||||
| - `COMPRESSION_LEVEL`: **-1**: Compression level | ||||
| 
 | ||||
| ### Conn log mode (`log.conn`, `log.conn.*` or `MODE=conn`) | ||||
| 
 | ||||
| - `RECONNECT_ON_MSG`: **false**: Reconnect host for every single message. | ||||
| - `RECONNECT`: **false**: Try to reconnect when connection is lost. | ||||
| - `PROTOCOL`: **tcp**: Set the protocol, either "tcp", "unix" or "udp". | ||||
| - `ADDR`: **:7020**: Sets the address to connect to. | ||||
| 
 | ||||
| ### SMTP log mode (`log.smtp`, `log.smtp.*` or `MODE=smtp`) | ||||
| 
 | ||||
| - `USER`: User email address to send from. | ||||
| - `PASSWD`: Password for the smtp server. | ||||
| - `HOST`: **127.0.0.1:25**: The SMTP host to connect to. | ||||
| - `RECEIVERS`: Email addresses to send to. | ||||
| - `SUBJECT`: **Diagnostic message from Gitea** | ||||
| 
 | ||||
| ## Cron (`cron`) | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										4
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								go.mod
									
									
									
									
									
								
							|  | @ -79,6 +79,7 @@ require ( | |||
| 	github.com/lunny/log v0.0.0-20160921050905-7887c61bf0de // indirect | ||||
| 	github.com/lunny/nodb v0.0.0-20160621015157-fc1ef06ad4af // indirect | ||||
| 	github.com/markbates/goth v1.49.0 | ||||
| 	github.com/mattn/go-isatty v0.0.7 | ||||
| 	github.com/mattn/go-oci8 v0.0.0-20190320171441-14ba190cf52d // indirect | ||||
| 	github.com/mattn/go-sqlite3 v1.10.0 | ||||
| 	github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect | ||||
|  | @ -114,8 +115,7 @@ require ( | |||
| 	golang.org/x/crypto v0.0.0-20180904163835-0709b304e793 | ||||
| 	golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519 | ||||
| 	golang.org/x/oauth2 v0.0.0-20181101160152-c453e0c75759 // indirect | ||||
| 	golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f | ||||
| 	golang.org/x/sys v0.0.0-20181026144532-2772b66316d2 | ||||
| 	golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 | ||||
| 	golang.org/x/text v0.3.0 | ||||
| 	gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect | ||||
| 	gopkg.in/asn1-ber.v1 v1.0.0-20150924051756-4e86f4367175 // indirect | ||||
|  |  | |||
							
								
								
									
										6
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										6
									
								
								go.sum
									
									
									
									
									
								
							|  | @ -207,6 +207,8 @@ github.com/lunny/nodb v0.0.0-20160621015157-fc1ef06ad4af/go.mod h1:Cqz6pqow14VOb | |||
| github.com/markbates/going v1.0.0/go.mod h1:I6mnB4BPnEeqo85ynXIx1ZFLLbtiLHNXVgWeFO9OGOA= | ||||
| github.com/markbates/goth v1.49.0 h1:qQ4Ti4WaqAxNAggOC+4s5M85sMVfMJwQn/Xkp73wfgI= | ||||
| github.com/markbates/goth v1.49.0/go.mod h1:zZmAw0Es0Dpm7TT/4AdN14QrkiWLMrrU9Xei1o+/mdA= | ||||
| github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc= | ||||
| github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= | ||||
| github.com/mattn/go-oci8 v0.0.0-20190320171441-14ba190cf52d h1:m+dSK37rFf2fqppZhg15yI2IwC9BtucBiRwSDm9VL8g= | ||||
| github.com/mattn/go-oci8 v0.0.0-20190320171441-14ba190cf52d/go.mod h1:/M9VLO+lUPmxvoOK2PfWRZ8mTtB4q1Hy9lEGijv9Nr8= | ||||
| github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= | ||||
|  | @ -311,8 +313,8 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6Zh | |||
| golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sys v0.0.0-20180903190138-2b024373dcd9/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||||
| golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||||
| golang.org/x/sys v0.0.0-20181026144532-2772b66316d2 h1:W7CqTdBJ1CmxLKe7LptKDnBYV6PHrVLiGnoyBjaG/JQ= | ||||
| golang.org/x/sys v0.0.0-20181026144532-2772b66316d2/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||||
| golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8= | ||||
| golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||||
| golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= | ||||
| golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | ||||
| golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= | ||||
|  |  | |||
							
								
								
									
										2
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								main.go
									
									
									
									
									
								
							|  | @ -56,7 +56,7 @@ arguments - which can alternatively be run by running the subcommand web.` | |||
| 	app.Action = cmd.CmdWeb.Action | ||||
| 	err := app.Run(os.Args) | ||||
| 	if err != nil { | ||||
| 		log.Fatal(4, "Failed to run app with %s: %v", os.Args, err) | ||||
| 		log.Fatal("Failed to run app with %s: %v", os.Args, err) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -110,7 +110,7 @@ func (a *Action) loadActUser() { | |||
| 	} else if IsErrUserNotExist(err) { | ||||
| 		a.ActUser = NewGhostUser() | ||||
| 	} else { | ||||
| 		log.Error(4, "GetUserByID(%d): %v", a.ActUserID, err) | ||||
| 		log.Error("GetUserByID(%d): %v", a.ActUserID, err) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | @ -121,7 +121,7 @@ func (a *Action) loadRepo() { | |||
| 	var err error | ||||
| 	a.Repo, err = GetRepositoryByID(a.RepoID) | ||||
| 	if err != nil { | ||||
| 		log.Error(4, "GetRepositoryByID(%d): %v", a.RepoID, err) | ||||
| 		log.Error("GetRepositoryByID(%d): %v", a.RepoID, err) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | @ -256,7 +256,7 @@ func (a *Action) GetIssueTitle() string { | |||
| 	index := com.StrTo(a.GetIssueInfos()[0]).MustInt64() | ||||
| 	issue, err := GetIssueByIndex(a.RepoID, index) | ||||
| 	if err != nil { | ||||
| 		log.Error(4, "GetIssueByIndex: %v", err) | ||||
| 		log.Error("GetIssueByIndex: %v", err) | ||||
| 		return "500 when get issue" | ||||
| 	} | ||||
| 	return issue.Title | ||||
|  | @ -268,7 +268,7 @@ func (a *Action) GetIssueContent() string { | |||
| 	index := com.StrTo(a.GetIssueInfos()[0]).MustInt64() | ||||
| 	issue, err := GetIssueByIndex(a.RepoID, index) | ||||
| 	if err != nil { | ||||
| 		log.Error(4, "GetIssueByIndex: %v", err) | ||||
| 		log.Error("GetIssueByIndex: %v", err) | ||||
| 		return "500 when get issue" | ||||
| 	} | ||||
| 	return issue.Content | ||||
|  | @ -419,7 +419,7 @@ func (pc *PushCommits) AvatarLink(email string) string { | |||
| 		if err != nil { | ||||
| 			pc.avatars[email] = base.AvatarLink(email) | ||||
| 			if !IsErrUserNotExist(err) { | ||||
| 				log.Error(4, "GetUserByEmail: %v", err) | ||||
| 				log.Error("GetUserByEmail: %v", err) | ||||
| 				return "" | ||||
| 			} | ||||
| 		} else { | ||||
|  | @ -619,7 +619,7 @@ func CommitRepoAction(opts CommitRepoActionOptions) error { | |||
| 		} | ||||
| 
 | ||||
| 		if err = UpdateIssuesCommit(pusher, repo, opts.Commits.Commits, refName); err != nil { | ||||
| 			log.Error(4, "updateIssuesCommit: %v", err) | ||||
| 			log.Error("updateIssuesCommit: %v", err) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
|  | @ -661,12 +661,12 @@ func CommitRepoAction(opts CommitRepoActionOptions) error { | |||
| 		if isNewBranch { | ||||
| 			gitRepo, err := git.OpenRepository(repo.RepoPath()) | ||||
| 			if err != nil { | ||||
| 				log.Error(4, "OpenRepository[%s]: %v", repo.RepoPath(), err) | ||||
| 				log.Error("OpenRepository[%s]: %v", repo.RepoPath(), err) | ||||
| 			} | ||||
| 
 | ||||
| 			shaSum, err = gitRepo.GetBranchCommitID(refName) | ||||
| 			if err != nil { | ||||
| 				log.Error(4, "GetBranchCommitID[%s]: %v", opts.RefFullName, err) | ||||
| 				log.Error("GetBranchCommitID[%s]: %v", opts.RefFullName, err) | ||||
| 			} | ||||
| 			if err = PrepareWebhooks(repo, HookEventCreate, &api.CreatePayload{ | ||||
| 				Ref:     refName, | ||||
|  | @ -697,11 +697,11 @@ func CommitRepoAction(opts CommitRepoActionOptions) error { | |||
| 
 | ||||
| 		gitRepo, err := git.OpenRepository(repo.RepoPath()) | ||||
| 		if err != nil { | ||||
| 			log.Error(4, "OpenRepository[%s]: %v", repo.RepoPath(), err) | ||||
| 			log.Error("OpenRepository[%s]: %v", repo.RepoPath(), err) | ||||
| 		} | ||||
| 		shaSum, err = gitRepo.GetTagCommitID(refName) | ||||
| 		if err != nil { | ||||
| 			log.Error(4, "GetTagCommitID[%s]: %v", opts.RefFullName, err) | ||||
| 			log.Error("GetTagCommitID[%s]: %v", opts.RefFullName, err) | ||||
| 		} | ||||
| 		if err = PrepareWebhooks(repo, HookEventCreate, &api.CreatePayload{ | ||||
| 			Ref:     refName, | ||||
|  |  | |||
|  | @ -65,7 +65,7 @@ func removeAllWithNotice(e Engine, title, path string) { | |||
| 		desc := fmt.Sprintf("%s [%s]: %v", title, path, err) | ||||
| 		log.Warn(desc) | ||||
| 		if err = createNotice(e, NoticeRepository, desc); err != nil { | ||||
| 			log.Error(4, "CreateRepositoryNotice: %v", err) | ||||
| 			log.Error("CreateRepositoryNotice: %v", err) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -61,7 +61,7 @@ func (protectBranch *ProtectedBranch) CanUserPush(userID int64) bool { | |||
| 
 | ||||
| 	in, err := IsUserInTeams(userID, protectBranch.WhitelistTeamIDs) | ||||
| 	if err != nil { | ||||
| 		log.Error(1, "IsUserInTeams:", err) | ||||
| 		log.Error("IsUserInTeams: %v", err) | ||||
| 		return false | ||||
| 	} | ||||
| 	return in | ||||
|  | @ -83,7 +83,7 @@ func (protectBranch *ProtectedBranch) CanUserMerge(userID int64) bool { | |||
| 
 | ||||
| 	in, err := IsUserInTeams(userID, protectBranch.MergeWhitelistTeamIDs) | ||||
| 	if err != nil { | ||||
| 		log.Error(1, "IsUserInTeams:", err) | ||||
| 		log.Error("IsUserInTeams: %v", err) | ||||
| 		return false | ||||
| 	} | ||||
| 	return in | ||||
|  | @ -101,7 +101,7 @@ func (protectBranch *ProtectedBranch) HasEnoughApprovals(pr *PullRequest) bool { | |||
| func (protectBranch *ProtectedBranch) GetGrantedApprovalsCount(pr *PullRequest) int64 { | ||||
| 	reviews, err := GetReviewersByPullID(pr.Issue.ID) | ||||
| 	if err != nil { | ||||
| 		log.Error(1, "GetReviewersByPullID:", err) | ||||
| 		log.Error("GetReviewersByPullID: %v", err) | ||||
| 		return 0 | ||||
| 	} | ||||
| 
 | ||||
|  | @ -119,7 +119,7 @@ func (protectBranch *ProtectedBranch) GetGrantedApprovalsCount(pr *PullRequest) | |||
| 	} | ||||
| 	approvalTeamCount, err := UsersInTeamsCount(userIDs, protectBranch.ApprovalsWhitelistTeamIDs) | ||||
| 	if err != nil { | ||||
| 		log.Error(1, "UsersInTeamsCount:", err) | ||||
| 		log.Error("UsersInTeamsCount: %v", err) | ||||
| 		return 0 | ||||
| 	} | ||||
| 	return approvalTeamCount + approvals | ||||
|  | @ -466,6 +466,6 @@ func RemoveOldDeletedBranches() { | |||
| 	deleteBefore := time.Now().Add(-setting.Cron.DeletedBranchesCleanup.OlderThan) | ||||
| 	_, err := x.Where("deleted_unix < ?", deleteBefore.Unix()).Delete(new(DeletedBranch)) | ||||
| 	if err != nil { | ||||
| 		log.Error(4, "DeletedBranchesCleanup: %v", err) | ||||
| 		log.Error("DeletedBranchesCleanup: %v", err) | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -52,7 +52,7 @@ func (key *GPGKey) BeforeInsert() { | |||
| func (key *GPGKey) AfterLoad(session *xorm.Session) { | ||||
| 	err := session.Where("primary_key_id=?", key.KeyID).Find(&key.SubsKey) | ||||
| 	if err != nil { | ||||
| 		log.Error(3, "Find Sub GPGkeys[%d]: %v", key.KeyID, err) | ||||
| 		log.Error("Find Sub GPGkeys[%s]: %v", key.KeyID, err) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | @ -364,7 +364,7 @@ func ParseCommitWithSignature(c *git.Commit) *CommitVerification { | |||
| 		//Parsing signature
 | ||||
| 		sig, err := extractSignature(c.Signature.Signature) | ||||
| 		if err != nil { //Skipping failed to extract sign
 | ||||
| 			log.Error(3, "SignatureRead err: %v", err) | ||||
| 			log.Error("SignatureRead err: %v", err) | ||||
| 			return &CommitVerification{ | ||||
| 				Verified: false, | ||||
| 				Reason:   "gpg.error.extract_sign", | ||||
|  | @ -377,7 +377,7 @@ func ParseCommitWithSignature(c *git.Commit) *CommitVerification { | |||
| 			// We can expect this to often be an ErrUserNotExist. in the case
 | ||||
| 			// it is not, however, it is important to log it.
 | ||||
| 			if !IsErrUserNotExist(err) { | ||||
| 				log.Error(3, "GetUserByEmail: %v", err) | ||||
| 				log.Error("GetUserByEmail: %v", err) | ||||
| 			} | ||||
| 			return &CommitVerification{ | ||||
| 				Verified: false, | ||||
|  | @ -387,7 +387,7 @@ func ParseCommitWithSignature(c *git.Commit) *CommitVerification { | |||
| 
 | ||||
| 		keys, err := ListGPGKeys(committer.ID) | ||||
| 		if err != nil { //Skipping failed to get gpg keys of user
 | ||||
| 			log.Error(3, "ListGPGKeys: %v", err) | ||||
| 			log.Error("ListGPGKeys: %v", err) | ||||
| 			return &CommitVerification{ | ||||
| 				Verified: false, | ||||
| 				Reason:   "gpg.error.failed_retrieval_gpg_keys", | ||||
|  | @ -411,7 +411,7 @@ func ParseCommitWithSignature(c *git.Commit) *CommitVerification { | |||
| 			//Generating hash of commit
 | ||||
| 			hash, err := populateHash(sig.Hash, []byte(c.Signature.Payload)) | ||||
| 			if err != nil { //Skipping ailed to generate hash
 | ||||
| 				log.Error(3, "PopulateHash: %v", err) | ||||
| 				log.Error("PopulateHash: %v", err) | ||||
| 				return &CommitVerification{ | ||||
| 					Verified: false, | ||||
| 					Reason:   "gpg.error.generate_hash", | ||||
|  | @ -432,7 +432,7 @@ func ParseCommitWithSignature(c *git.Commit) *CommitVerification { | |||
| 				//Generating hash of commit
 | ||||
| 				hash, err := populateHash(sig.Hash, []byte(c.Signature.Payload)) | ||||
| 				if err != nil { //Skipping ailed to generate hash
 | ||||
| 					log.Error(3, "PopulateHash: %v", err) | ||||
| 					log.Error("PopulateHash: %v", err) | ||||
| 					return &CommitVerification{ | ||||
| 						Verified: false, | ||||
| 						Reason:   "gpg.error.generate_hash", | ||||
|  |  | |||
|  | @ -112,7 +112,7 @@ func (issue *Issue) IsTimetrackerEnabled() bool { | |||
| 
 | ||||
| func (issue *Issue) isTimetrackerEnabled(e Engine) bool { | ||||
| 	if err := issue.loadRepo(e); err != nil { | ||||
| 		log.Error(4, fmt.Sprintf("loadRepo: %v", err)) | ||||
| 		log.Error(fmt.Sprintf("loadRepo: %v", err)) | ||||
| 		return false | ||||
| 	} | ||||
| 	return issue.Repo.IsTimetrackerEnabled() | ||||
|  | @ -423,23 +423,23 @@ func (issue *Issue) sendLabelUpdatedWebhook(doer *User) { | |||
| 	var err error | ||||
| 
 | ||||
| 	if err = issue.loadRepo(x); err != nil { | ||||
| 		log.Error(4, "loadRepo: %v", err) | ||||
| 		log.Error("loadRepo: %v", err) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	if err = issue.loadPoster(x); err != nil { | ||||
| 		log.Error(4, "loadPoster: %v", err) | ||||
| 		log.Error("loadPoster: %v", err) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	mode, _ := AccessLevel(issue.Poster, issue.Repo) | ||||
| 	if issue.IsPull { | ||||
| 		if err = issue.loadPullRequest(x); err != nil { | ||||
| 			log.Error(4, "loadPullRequest: %v", err) | ||||
| 			log.Error("loadPullRequest: %v", err) | ||||
| 			return | ||||
| 		} | ||||
| 		if err = issue.PullRequest.LoadIssue(); err != nil { | ||||
| 			log.Error(4, "LoadIssue: %v", err) | ||||
| 			log.Error("LoadIssue: %v", err) | ||||
| 			return | ||||
| 		} | ||||
| 		err = PrepareWebhooks(issue.Repo, HookEventPullRequest, &api.PullRequestPayload{ | ||||
|  | @ -459,7 +459,7 @@ func (issue *Issue) sendLabelUpdatedWebhook(doer *User) { | |||
| 		}) | ||||
| 	} | ||||
| 	if err != nil { | ||||
| 		log.Error(4, "PrepareWebhooks [is_pull: %v]: %v", issue.IsPull, err) | ||||
| 		log.Error("PrepareWebhooks [is_pull: %v]: %v", issue.IsPull, err) | ||||
| 	} else { | ||||
| 		go HookQueue.Add(issue.RepoID) | ||||
| 	} | ||||
|  | @ -584,7 +584,7 @@ func (issue *Issue) ClearLabels(doer *User) (err error) { | |||
| 	if issue.IsPull { | ||||
| 		err = issue.PullRequest.LoadIssue() | ||||
| 		if err != nil { | ||||
| 			log.Error(4, "LoadIssue: %v", err) | ||||
| 			log.Error("LoadIssue: %v", err) | ||||
| 			return | ||||
| 		} | ||||
| 		err = PrepareWebhooks(issue.Repo, HookEventPullRequest, &api.PullRequestPayload{ | ||||
|  | @ -604,7 +604,7 @@ func (issue *Issue) ClearLabels(doer *User) (err error) { | |||
| 		}) | ||||
| 	} | ||||
| 	if err != nil { | ||||
| 		log.Error(4, "PrepareWebhooks [is_pull: %v]: %v", issue.IsPull, err) | ||||
| 		log.Error("PrepareWebhooks [is_pull: %v]: %v", issue.IsPull, err) | ||||
| 	} else { | ||||
| 		go HookQueue.Add(issue.RepoID) | ||||
| 	} | ||||
|  | @ -819,7 +819,7 @@ func (issue *Issue) ChangeStatus(doer *User, isClosed bool) (err error) { | |||
| 		err = PrepareWebhooks(issue.Repo, HookEventIssues, apiIssue) | ||||
| 	} | ||||
| 	if err != nil { | ||||
| 		log.Error(4, "PrepareWebhooks [is_pull: %v, is_closed: %v]: %v", issue.IsPull, isClosed, err) | ||||
| 		log.Error("PrepareWebhooks [is_pull: %v, is_closed: %v]: %v", issue.IsPull, isClosed, err) | ||||
| 	} else { | ||||
| 		go HookQueue.Add(issue.Repo.ID) | ||||
| 	} | ||||
|  | @ -888,7 +888,7 @@ func (issue *Issue) ChangeTitle(doer *User, title string) (err error) { | |||
| 	} | ||||
| 
 | ||||
| 	if err != nil { | ||||
| 		log.Error(4, "PrepareWebhooks [is_pull: %v]: %v", issue.IsPull, err) | ||||
| 		log.Error("PrepareWebhooks [is_pull: %v]: %v", issue.IsPull, err) | ||||
| 	} else { | ||||
| 		go HookQueue.Add(issue.RepoID) | ||||
| 	} | ||||
|  | @ -953,7 +953,7 @@ func (issue *Issue) ChangeContent(doer *User, content string) (err error) { | |||
| 		}) | ||||
| 	} | ||||
| 	if err != nil { | ||||
| 		log.Error(4, "PrepareWebhooks [is_pull: %v]: %v", issue.IsPull, err) | ||||
| 		log.Error("PrepareWebhooks [is_pull: %v]: %v", issue.IsPull, err) | ||||
| 	} else { | ||||
| 		go HookQueue.Add(issue.RepoID) | ||||
| 	} | ||||
|  | @ -1169,7 +1169,7 @@ func NewIssue(repo *Repository, issue *Issue, labelIDs []int64, assigneeIDs []in | |||
| 		Repo:      repo, | ||||
| 		IsPrivate: repo.IsPrivate, | ||||
| 	}); err != nil { | ||||
| 		log.Error(4, "NotifyWatchers: %v", err) | ||||
| 		log.Error("NotifyWatchers: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	mode, _ := AccessLevel(issue.Poster, issue.Repo) | ||||
|  | @ -1180,7 +1180,7 @@ func NewIssue(repo *Repository, issue *Issue, labelIDs []int64, assigneeIDs []in | |||
| 		Repository: repo.APIFormat(mode), | ||||
| 		Sender:     issue.Poster.APIFormat(), | ||||
| 	}); err != nil { | ||||
| 		log.Error(4, "PrepareWebhooks: %v", err) | ||||
| 		log.Error("PrepareWebhooks: %v", err) | ||||
| 	} else { | ||||
| 		go HookQueue.Add(issue.RepoID) | ||||
| 	} | ||||
|  |  | |||
|  | @ -187,7 +187,7 @@ func (issue *Issue) changeAssignee(sess *xorm.Session, doer *User, assigneeID in | |||
| 			apiPullRequest.Action = api.HookIssueAssigned | ||||
| 		} | ||||
| 		if err := prepareWebhooks(sess, issue.Repo, HookEventPullRequest, apiPullRequest); err != nil { | ||||
| 			log.Error(4, "PrepareWebhooks [is_pull: %v, remove_assignee: %v]: %v", issue.IsPull, removed, err) | ||||
| 			log.Error("PrepareWebhooks [is_pull: %v, remove_assignee: %v]: %v", issue.IsPull, removed, err) | ||||
| 			return nil | ||||
| 		} | ||||
| 	} else { | ||||
|  | @ -205,7 +205,7 @@ func (issue *Issue) changeAssignee(sess *xorm.Session, doer *User, assigneeID in | |||
| 			apiIssue.Action = api.HookIssueAssigned | ||||
| 		} | ||||
| 		if err := prepareWebhooks(sess, issue.Repo, HookEventIssues, apiIssue); err != nil { | ||||
| 			log.Error(4, "PrepareWebhooks [is_pull: %v, remove_assignee: %v]: %v", issue.IsPull, removed, err) | ||||
| 			log.Error("PrepareWebhooks [is_pull: %v, remove_assignee: %v]: %v", issue.IsPull, removed, err) | ||||
| 			return nil | ||||
| 		} | ||||
| 	} | ||||
|  |  | |||
|  | @ -171,12 +171,12 @@ func (c *Comment) AfterDelete() { | |||
| func (c *Comment) HTMLURL() string { | ||||
| 	err := c.LoadIssue() | ||||
| 	if err != nil { // Silently dropping errors :unamused:
 | ||||
| 		log.Error(4, "LoadIssue(%d): %v", c.IssueID, err) | ||||
| 		log.Error("LoadIssue(%d): %v", c.IssueID, err) | ||||
| 		return "" | ||||
| 	} | ||||
| 	err = c.Issue.loadRepo(x) | ||||
| 	if err != nil { // Silently dropping errors :unamused:
 | ||||
| 		log.Error(4, "loadRepo(%d): %v", c.Issue.RepoID, err) | ||||
| 		log.Error("loadRepo(%d): %v", c.Issue.RepoID, err) | ||||
| 		return "" | ||||
| 	} | ||||
| 	if c.Type == CommentTypeCode { | ||||
|  | @ -200,7 +200,7 @@ func (c *Comment) HTMLURL() string { | |||
| func (c *Comment) IssueURL() string { | ||||
| 	err := c.LoadIssue() | ||||
| 	if err != nil { // Silently dropping errors :unamused:
 | ||||
| 		log.Error(4, "LoadIssue(%d): %v", c.IssueID, err) | ||||
| 		log.Error("LoadIssue(%d): %v", c.IssueID, err) | ||||
| 		return "" | ||||
| 	} | ||||
| 
 | ||||
|  | @ -210,7 +210,7 @@ func (c *Comment) IssueURL() string { | |||
| 
 | ||||
| 	err = c.Issue.loadRepo(x) | ||||
| 	if err != nil { // Silently dropping errors :unamused:
 | ||||
| 		log.Error(4, "loadRepo(%d): %v", c.Issue.RepoID, err) | ||||
| 		log.Error("loadRepo(%d): %v", c.Issue.RepoID, err) | ||||
| 		return "" | ||||
| 	} | ||||
| 	return c.Issue.HTMLURL() | ||||
|  | @ -220,13 +220,13 @@ func (c *Comment) IssueURL() string { | |||
| func (c *Comment) PRURL() string { | ||||
| 	err := c.LoadIssue() | ||||
| 	if err != nil { // Silently dropping errors :unamused:
 | ||||
| 		log.Error(4, "LoadIssue(%d): %v", c.IssueID, err) | ||||
| 		log.Error("LoadIssue(%d): %v", c.IssueID, err) | ||||
| 		return "" | ||||
| 	} | ||||
| 
 | ||||
| 	err = c.Issue.loadRepo(x) | ||||
| 	if err != nil { // Silently dropping errors :unamused:
 | ||||
| 		log.Error(4, "loadRepo(%d): %v", c.Issue.RepoID, err) | ||||
| 		log.Error("loadRepo(%d): %v", c.Issue.RepoID, err) | ||||
| 		return "" | ||||
| 	} | ||||
| 
 | ||||
|  | @ -318,7 +318,7 @@ func (c *Comment) LoadPoster() error { | |||
| 			c.PosterID = -1 | ||||
| 			c.Poster = NewGhostUser() | ||||
| 		} else { | ||||
| 			log.Error(3, "getUserByID[%d]: %v", c.ID, err) | ||||
| 			log.Error("getUserByID[%d]: %v", c.ID, err) | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
|  | @ -333,7 +333,7 @@ func (c *Comment) LoadAttachments() error { | |||
| 	var err error | ||||
| 	c.Attachments, err = getAttachmentsByCommentID(x, c.ID) | ||||
| 	if err != nil { | ||||
| 		log.Error(3, "getAttachmentsByCommentID[%d]: %v", c.ID, err) | ||||
| 		log.Error("getAttachmentsByCommentID[%d]: %v", c.ID, err) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | @ -384,7 +384,7 @@ func (c *Comment) mailParticipants(e Engine, opType ActionType, issue *Issue) (e | |||
| 		content = fmt.Sprintf("Reopened #%d", issue.Index) | ||||
| 	} | ||||
| 	if err = mailIssueCommentToParticipants(e, issue, c.Poster, content, c, mentions); err != nil { | ||||
| 		log.Error(4, "mailIssueCommentToParticipants: %v", err) | ||||
| 		log.Error("mailIssueCommentToParticipants: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
|  | @ -492,12 +492,12 @@ func (c *Comment) MustAsDiff() *Diff { | |||
| func (c *Comment) CodeCommentURL() string { | ||||
| 	err := c.LoadIssue() | ||||
| 	if err != nil { // Silently dropping errors :unamused:
 | ||||
| 		log.Error(4, "LoadIssue(%d): %v", c.IssueID, err) | ||||
| 		log.Error("LoadIssue(%d): %v", c.IssueID, err) | ||||
| 		return "" | ||||
| 	} | ||||
| 	err = c.Issue.loadRepo(x) | ||||
| 	if err != nil { // Silently dropping errors :unamused:
 | ||||
| 		log.Error(4, "loadRepo(%d): %v", c.Issue.RepoID, err) | ||||
| 		log.Error("loadRepo(%d): %v", c.Issue.RepoID, err) | ||||
| 		return "" | ||||
| 	} | ||||
| 	return fmt.Sprintf("%s/files#%s", c.Issue.HTMLURL(), c.HashTag()) | ||||
|  | @ -638,7 +638,7 @@ func sendCreateCommentAction(e *xorm.Session, opts *CreateCommentOptions, commen | |||
| 	// Notify watchers for whatever action comes in, ignore if no action type.
 | ||||
| 	if act.OpType > 0 { | ||||
| 		if err = notifyWatchers(e, act); err != nil { | ||||
| 			log.Error(4, "notifyWatchers: %v", err) | ||||
| 			log.Error("notifyWatchers: %v", err) | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
|  | @ -850,7 +850,7 @@ func CreateIssueComment(doer *User, repo *Repository, issue *Issue, content stri | |||
| 		Repository: repo.APIFormat(mode), | ||||
| 		Sender:     doer.APIFormat(), | ||||
| 	}); err != nil { | ||||
| 		log.Error(2, "PrepareWebhooks [comment_id: %d]: %v", comment.ID, err) | ||||
| 		log.Error("PrepareWebhooks [comment_id: %d]: %v", comment.ID, err) | ||||
| 	} else { | ||||
| 		go HookQueue.Add(repo.ID) | ||||
| 	} | ||||
|  | @ -1053,7 +1053,7 @@ func UpdateComment(doer *User, c *Comment, oldContent string) error { | |||
| 		Repository: c.Issue.Repo.APIFormat(mode), | ||||
| 		Sender:     doer.APIFormat(), | ||||
| 	}); err != nil { | ||||
| 		log.Error(2, "PrepareWebhooks [comment_id: %d]: %v", c.ID, err) | ||||
| 		log.Error("PrepareWebhooks [comment_id: %d]: %v", c.ID, err) | ||||
| 	} else { | ||||
| 		go HookQueue.Add(c.Issue.Repo.ID) | ||||
| 	} | ||||
|  | @ -1108,7 +1108,7 @@ func DeleteComment(doer *User, comment *Comment) error { | |||
| 		Repository: comment.Issue.Repo.APIFormat(mode), | ||||
| 		Sender:     doer.APIFormat(), | ||||
| 	}); err != nil { | ||||
| 		log.Error(2, "PrepareWebhooks [comment_id: %d]: %v", comment.ID, err) | ||||
| 		log.Error("PrepareWebhooks [comment_id: %d]: %v", comment.ID, err) | ||||
| 	} else { | ||||
| 		go HookQueue.Add(comment.Issue.Repo.ID) | ||||
| 	} | ||||
|  |  | |||
|  | @ -129,7 +129,7 @@ func (issue *Issue) mailParticipants(e Engine) (err error) { | |||
| 	} | ||||
| 
 | ||||
| 	if err = mailIssueCommentToParticipants(e, issue, issue.Poster, issue.Content, nil, mentions); err != nil { | ||||
| 		log.Error(4, "mailIssueCommentToParticipants: %v", err) | ||||
| 		log.Error("mailIssueCommentToParticipants: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
|  |  | |||
|  | @ -393,7 +393,7 @@ func ChangeMilestoneAssign(issue *Issue, doer *User, oldMilestoneID int64) (err | |||
| 	if issue.IsPull { | ||||
| 		err = issue.PullRequest.LoadIssue() | ||||
| 		if err != nil { | ||||
| 			log.Error(2, "LoadIssue: %v", err) | ||||
| 			log.Error("LoadIssue: %v", err) | ||||
| 			return | ||||
| 		} | ||||
| 		err = PrepareWebhooks(issue.Repo, HookEventPullRequest, &api.PullRequestPayload{ | ||||
|  | @ -413,7 +413,7 @@ func ChangeMilestoneAssign(issue *Issue, doer *User, oldMilestoneID int64) (err | |||
| 		}) | ||||
| 	} | ||||
| 	if err != nil { | ||||
| 		log.Error(2, "PrepareWebhooks [is_pull: %v]: %v", issue.IsPull, err) | ||||
| 		log.Error("PrepareWebhooks [is_pull: %v]: %v", issue.IsPull, err) | ||||
| 	} else { | ||||
| 		go HookQueue.Add(issue.RepoID) | ||||
| 	} | ||||
|  |  | |||
|  | @ -39,11 +39,11 @@ func (l *LFSLock) AfterLoad(session *xorm.Session) { | |||
| 	var err error | ||||
| 	l.Owner, err = getUserByID(session, l.OwnerID) | ||||
| 	if err != nil { | ||||
| 		log.Error(2, "LFS lock AfterLoad failed OwnerId[%d] not found: %v", l.OwnerID, err) | ||||
| 		log.Error("LFS lock AfterLoad failed OwnerId[%d] not found: %v", l.OwnerID, err) | ||||
| 	} | ||||
| 	l.Repo, err = getRepositoryByID(session, l.RepoID) | ||||
| 	if err != nil { | ||||
| 		log.Error(2, "LFS lock AfterLoad failed RepoId[%d] not found: %v", l.RepoID, err) | ||||
| 		log.Error("LFS lock AfterLoad failed RepoId[%d] not found: %v", l.RepoID, err) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -56,7 +56,7 @@ func SendUserMail(c *macaron.Context, u *User, tpl base.TplName, code, subject, | |||
| 	var content bytes.Buffer | ||||
| 
 | ||||
| 	if err := templates.ExecuteTemplate(&content, string(tpl), data); err != nil { | ||||
| 		log.Error(3, "Template: %v", err) | ||||
| 		log.Error("Template: %v", err) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
|  | @ -88,7 +88,7 @@ func SendActivateEmailMail(c *macaron.Context, u *User, email *EmailAddress) { | |||
| 	var content bytes.Buffer | ||||
| 
 | ||||
| 	if err := templates.ExecuteTemplate(&content, string(mailAuthActivateEmail), data); err != nil { | ||||
| 		log.Error(3, "Template: %v", err) | ||||
| 		log.Error("Template: %v", err) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
|  | @ -107,7 +107,7 @@ func SendRegisterNotifyMail(c *macaron.Context, u *User) { | |||
| 	var content bytes.Buffer | ||||
| 
 | ||||
| 	if err := templates.ExecuteTemplate(&content, string(mailAuthRegisterNotify), data); err != nil { | ||||
| 		log.Error(3, "Template: %v", err) | ||||
| 		log.Error("Template: %v", err) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
|  | @ -131,7 +131,7 @@ func SendCollaboratorMail(u, doer *User, repo *Repository) { | |||
| 	var content bytes.Buffer | ||||
| 
 | ||||
| 	if err := templates.ExecuteTemplate(&content, string(mailNotifyCollaborator), data); err != nil { | ||||
| 		log.Error(3, "Template: %v", err) | ||||
| 		log.Error("Template: %v", err) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
|  | @ -165,7 +165,7 @@ func composeIssueCommentMessage(issue *Issue, doer *User, content string, commen | |||
| 	var mailBody bytes.Buffer | ||||
| 
 | ||||
| 	if err := templates.ExecuteTemplate(&mailBody, string(tplName), data); err != nil { | ||||
| 		log.Error(3, "Template: %v", err) | ||||
| 		log.Error("Template: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	msg := mailer.NewMessageFrom(tos, doer.DisplayName(), setting.MailService.FromEmail, subject, mailBody.String()) | ||||
|  |  | |||
|  | @ -244,7 +244,7 @@ func Migrate(x *xorm.Engine) error { | |||
| 
 | ||||
| 	v := currentVersion.Version | ||||
| 	if minDBVersion > v { | ||||
| 		log.Fatal(4, `Gitea no longer supports auto-migration from your previously installed version. | ||||
| 		log.Fatal(`Gitea no longer supports auto-migration from your previously installed version. | ||||
| Please try to upgrade to a lower version (>= v0.6.0) first, then upgrade to current version.`) | ||||
| 		return nil | ||||
| 	} | ||||
|  | @ -315,7 +315,7 @@ func dropTableColumns(sess *xorm.Session, tableName string, columnNames ...strin | |||
| 
 | ||||
| 		return sess.Commit() | ||||
| 	default: | ||||
| 		log.Fatal(4, "Unrecognized DB") | ||||
| 		log.Fatal("Unrecognized DB") | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
|  |  | |||
|  | @ -26,7 +26,7 @@ func removeActionColumns(x *xorm.Engine) error { | |||
| 			return fmt.Errorf("DROP COLUMN repo_name: %v", err) | ||||
| 		} | ||||
| 	default: | ||||
| 		log.Fatal(4, "Unrecognized DB") | ||||
| 		log.Fatal("Unrecognized DB") | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  |  | |||
|  | @ -21,7 +21,7 @@ func removeIndexColumnFromRepoUnitTable(x *xorm.Engine) (err error) { | |||
| 			log.Warn("DROP COLUMN index: %v", err) | ||||
| 		} | ||||
| 	default: | ||||
| 		log.Fatal(4, "Unrecognized DB") | ||||
| 		log.Fatal("Unrecognized DB") | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
|  |  | |||
|  | @ -48,7 +48,7 @@ func migrateProtectedBranchStruct(x *xorm.Engine) error { | |||
| 			log.Warn("DROP COLUMN can_push (skipping): %v", err) | ||||
| 		} | ||||
| 	default: | ||||
| 		log.Fatal(4, "Unrecognized DB") | ||||
| 		log.Fatal("Unrecognized DB") | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
|  |  | |||
|  | @ -33,7 +33,7 @@ func addSizeToAttachment(x *xorm.Engine) error { | |||
| 		localPath := path.Join(setting.AttachmentPath, attach.UUID[0:1], attach.UUID[1:2], attach.UUID) | ||||
| 		fi, err := os.Stat(localPath) | ||||
| 		if err != nil { | ||||
| 			log.Error(4, "calculate file size of attachment[UUID: %s]: %v", attach.UUID, err) | ||||
| 			log.Error("calculate file size of attachment[UUID: %s]: %v", attach.UUID, err) | ||||
| 			continue | ||||
| 		} | ||||
| 		attach.Size = fi.Size() | ||||
|  |  | |||
|  | @ -303,7 +303,7 @@ func isOrganizationOwner(e Engine, orgID, uid int64) (bool, error) { | |||
| 	if has, err := e.Get(ownerTeam); err != nil { | ||||
| 		return false, err | ||||
| 	} else if !has { | ||||
| 		log.Error(4, "Organization does not have owner team: %d", orgID) | ||||
| 		log.Error("Organization does not have owner team: %d", orgID) | ||||
| 		return false, nil | ||||
| 	} | ||||
| 	return isTeamMember(e, orgID, ownerTeam.ID, uid) | ||||
|  |  | |||
|  | @ -69,7 +69,7 @@ func (t *Team) IsOwnerTeam() bool { | |||
| func (t *Team) IsMember(userID int64) bool { | ||||
| 	isMember, err := IsTeamMember(t.OrgID, t.ID, userID) | ||||
| 	if err != nil { | ||||
| 		log.Error(4, "IsMember: %v", err) | ||||
| 		log.Error("IsMember: %v", err) | ||||
| 		return false | ||||
| 	} | ||||
| 	return isMember | ||||
|  |  | |||
|  | @ -135,7 +135,7 @@ func (pr *PullRequest) GetDefaultMergeMessage() string { | |||
| 		var err error | ||||
| 		pr.HeadRepo, err = GetRepositoryByID(pr.HeadRepoID) | ||||
| 		if err != nil { | ||||
| 			log.Error(4, "GetRepositoryById[%d]: %v", pr.HeadRepoID, err) | ||||
| 			log.Error("GetRepositoryById[%d]: %v", pr.HeadRepoID, err) | ||||
| 			return "" | ||||
| 		} | ||||
| 	} | ||||
|  | @ -145,7 +145,7 @@ func (pr *PullRequest) GetDefaultMergeMessage() string { | |||
| // GetDefaultSquashMessage returns default message used when squash and merging pull request
 | ||||
| func (pr *PullRequest) GetDefaultSquashMessage() string { | ||||
| 	if err := pr.LoadIssue(); err != nil { | ||||
| 		log.Error(4, "LoadIssue: %v", err) | ||||
| 		log.Error("LoadIssue: %v", err) | ||||
| 		return "" | ||||
| 	} | ||||
| 	return fmt.Sprintf("%s (#%d)", pr.Issue.Title, pr.Issue.Index) | ||||
|  | @ -172,21 +172,21 @@ func (pr *PullRequest) apiFormat(e Engine) *api.PullRequest { | |||
| 		err        error | ||||
| 	) | ||||
| 	if err = pr.Issue.loadRepo(e); err != nil { | ||||
| 		log.Error(log.ERROR, "loadRepo[%d]: %v", pr.ID, err) | ||||
| 		log.Error("loadRepo[%d]: %v", pr.ID, err) | ||||
| 		return nil | ||||
| 	} | ||||
| 	apiIssue := pr.Issue.apiFormat(e) | ||||
| 	if pr.BaseRepo == nil { | ||||
| 		pr.BaseRepo, err = getRepositoryByID(e, pr.BaseRepoID) | ||||
| 		if err != nil { | ||||
| 			log.Error(log.ERROR, "GetRepositoryById[%d]: %v", pr.ID, err) | ||||
| 			log.Error("GetRepositoryById[%d]: %v", pr.ID, err) | ||||
| 			return nil | ||||
| 		} | ||||
| 	} | ||||
| 	if pr.HeadRepo == nil { | ||||
| 		pr.HeadRepo, err = getRepositoryByID(e, pr.HeadRepoID) | ||||
| 		if err != nil { | ||||
| 			log.Error(log.ERROR, "GetRepositoryById[%d]: %v", pr.ID, err) | ||||
| 			log.Error("GetRepositoryById[%d]: %v", pr.ID, err) | ||||
| 			return nil | ||||
| 		} | ||||
| 	} | ||||
|  | @ -581,11 +581,11 @@ func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository, mergeStyle | |||
| 	pr.MergerID = doer.ID | ||||
| 
 | ||||
| 	if err = pr.setMerged(); err != nil { | ||||
| 		log.Error(4, "setMerged [%d]: %v", pr.ID, err) | ||||
| 		log.Error("setMerged [%d]: %v", pr.ID, err) | ||||
| 	} | ||||
| 
 | ||||
| 	if err = MergePullRequestAction(doer, pr.Issue.Repo, pr.Issue); err != nil { | ||||
| 		log.Error(4, "MergePullRequestAction [%d]: %v", pr.ID, err) | ||||
| 		log.Error("MergePullRequestAction [%d]: %v", pr.ID, err) | ||||
| 	} | ||||
| 
 | ||||
| 	// Reset cached commit count
 | ||||
|  | @ -593,7 +593,7 @@ func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository, mergeStyle | |||
| 
 | ||||
| 	// Reload pull request information.
 | ||||
| 	if err = pr.LoadAttributes(); err != nil { | ||||
| 		log.Error(4, "LoadAttributes: %v", err) | ||||
| 		log.Error("LoadAttributes: %v", err) | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
|  | @ -605,14 +605,14 @@ func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository, mergeStyle | |||
| 		Repository:  pr.Issue.Repo.APIFormat(mode), | ||||
| 		Sender:      doer.APIFormat(), | ||||
| 	}); err != nil { | ||||
| 		log.Error(4, "PrepareWebhooks: %v", err) | ||||
| 		log.Error("PrepareWebhooks: %v", err) | ||||
| 	} else { | ||||
| 		go HookQueue.Add(pr.Issue.Repo.ID) | ||||
| 	} | ||||
| 
 | ||||
| 	l, err := baseGitRepo.CommitsBetweenIDs(pr.MergedCommitID, pr.MergeBase) | ||||
| 	if err != nil { | ||||
| 		log.Error(4, "CommitsBetweenIDs: %v", err) | ||||
| 		log.Error("CommitsBetweenIDs: %v", err) | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
|  | @ -621,7 +621,7 @@ func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository, mergeStyle | |||
| 	// to avoid strange diff commits produced.
 | ||||
| 	mergeCommit, err := baseGitRepo.GetBranchCommit(pr.BaseBranch) | ||||
| 	if err != nil { | ||||
| 		log.Error(4, "GetBranchCommit: %v", err) | ||||
| 		log.Error("GetBranchCommit: %v", err) | ||||
| 		return nil | ||||
| 	} | ||||
| 	if mergeStyle == MergeStyleMerge { | ||||
|  | @ -639,7 +639,7 @@ func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository, mergeStyle | |||
| 		Sender:     doer.APIFormat(), | ||||
| 	} | ||||
| 	if err = PrepareWebhooks(pr.BaseRepo, HookEventPush, p); err != nil { | ||||
| 		log.Error(4, "PrepareWebhooks: %v", err) | ||||
| 		log.Error("PrepareWebhooks: %v", err) | ||||
| 	} else { | ||||
| 		go HookQueue.Add(pr.BaseRepo.ID) | ||||
| 	} | ||||
|  | @ -692,7 +692,7 @@ func (pr *PullRequest) setMerged() (err error) { | |||
| func (pr *PullRequest) manuallyMerged() bool { | ||||
| 	commit, err := pr.getMergeCommit() | ||||
| 	if err != nil { | ||||
| 		log.Error(4, "PullRequest[%d].getMergeCommit: %v", pr.ID, err) | ||||
| 		log.Error("PullRequest[%d].getMergeCommit: %v", pr.ID, err) | ||||
| 		return false | ||||
| 	} | ||||
| 	if commit != nil { | ||||
|  | @ -705,7 +705,7 @@ func (pr *PullRequest) manuallyMerged() bool { | |||
| 		if merger == nil { | ||||
| 			if pr.BaseRepo.Owner == nil { | ||||
| 				if err = pr.BaseRepo.getOwner(x); err != nil { | ||||
| 					log.Error(4, "BaseRepo.getOwner[%d]: %v", pr.ID, err) | ||||
| 					log.Error("BaseRepo.getOwner[%d]: %v", pr.ID, err) | ||||
| 					return false | ||||
| 				} | ||||
| 			} | ||||
|  | @ -715,7 +715,7 @@ func (pr *PullRequest) manuallyMerged() bool { | |||
| 		pr.MergerID = merger.ID | ||||
| 
 | ||||
| 		if err = pr.setMerged(); err != nil { | ||||
| 			log.Error(4, "PullRequest[%d].setMerged : %v", pr.ID, err) | ||||
| 			log.Error("PullRequest[%d].setMerged : %v", pr.ID, err) | ||||
| 			return false | ||||
| 		} | ||||
| 		log.Info("manuallyMerged[%d]: Marked as manually merged into %s/%s by commit id: %s", pr.ID, pr.BaseRepo.Name, pr.BaseBranch, commit.ID.String()) | ||||
|  | @ -936,7 +936,7 @@ func NewPullRequest(repo *Repository, pull *Issue, labelIDs []int64, uuids []str | |||
| 		Repo:      repo, | ||||
| 		IsPrivate: repo.IsPrivate, | ||||
| 	}); err != nil { | ||||
| 		log.Error(4, "NotifyWatchers: %v", err) | ||||
| 		log.Error("NotifyWatchers: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	pr.Issue = pull | ||||
|  | @ -949,7 +949,7 @@ func NewPullRequest(repo *Repository, pull *Issue, labelIDs []int64, uuids []str | |||
| 		Repository:  repo.APIFormat(mode), | ||||
| 		Sender:      pull.Poster.APIFormat(), | ||||
| 	}); err != nil { | ||||
| 		log.Error(4, "PrepareWebhooks: %v", err) | ||||
| 		log.Error("PrepareWebhooks: %v", err) | ||||
| 	} else { | ||||
| 		go HookQueue.Add(repo.ID) | ||||
| 	} | ||||
|  | @ -997,12 +997,12 @@ func PullRequests(baseRepoID int64, opts *PullRequestsOptions) ([]*PullRequest, | |||
| 
 | ||||
| 	countSession, err := listPullRequestStatement(baseRepoID, opts) | ||||
| 	if err != nil { | ||||
| 		log.Error(4, "listPullRequestStatement", err) | ||||
| 		log.Error("listPullRequestStatement: %v", err) | ||||
| 		return nil, 0, err | ||||
| 	} | ||||
| 	maxResults, err := countSession.Count(new(PullRequest)) | ||||
| 	if err != nil { | ||||
| 		log.Error(4, "Count PRs", err) | ||||
| 		log.Error("Count PRs: %v", err) | ||||
| 		return nil, maxResults, err | ||||
| 	} | ||||
| 
 | ||||
|  | @ -1010,7 +1010,7 @@ func PullRequests(baseRepoID int64, opts *PullRequestsOptions) ([]*PullRequest, | |||
| 	findSession, err := listPullRequestStatement(baseRepoID, opts) | ||||
| 	sortIssuesSession(findSession, opts.SortType) | ||||
| 	if err != nil { | ||||
| 		log.Error(4, "listPullRequestStatement", err) | ||||
| 		log.Error("listPullRequestStatement: %v", err) | ||||
| 		return nil, maxResults, err | ||||
| 	} | ||||
| 	findSession.Limit(ItemsPerPage, (opts.Page-1)*ItemsPerPage) | ||||
|  | @ -1215,7 +1215,7 @@ func (pr *PullRequest) AddToTaskQueue() { | |||
| 	go pullRequestQueue.AddFunc(pr.ID, func() { | ||||
| 		pr.Status = PullRequestStatusChecking | ||||
| 		if err := pr.UpdateCols("status"); err != nil { | ||||
| 			log.Error(5, "AddToTaskQueue.UpdateCols[%d].(add to queue): %v", pr.ID, err) | ||||
| 			log.Error("AddToTaskQueue.UpdateCols[%d].(add to queue): %v", pr.ID, err) | ||||
| 		} | ||||
| 	}) | ||||
| } | ||||
|  | @ -1290,10 +1290,10 @@ func addHeadRepoTasks(prs []*PullRequest) { | |||
| 	for _, pr := range prs { | ||||
| 		log.Trace("addHeadRepoTasks[%d]: composing new test task", pr.ID) | ||||
| 		if err := pr.UpdatePatch(); err != nil { | ||||
| 			log.Error(4, "UpdatePatch: %v", err) | ||||
| 			log.Error("UpdatePatch: %v", err) | ||||
| 			continue | ||||
| 		} else if err := pr.PushToBaseRepo(); err != nil { | ||||
| 			log.Error(4, "PushToBaseRepo: %v", err) | ||||
| 			log.Error("PushToBaseRepo: %v", err) | ||||
| 			continue | ||||
| 		} | ||||
| 
 | ||||
|  | @ -1307,23 +1307,23 @@ func AddTestPullRequestTask(doer *User, repoID int64, branch string, isSync bool | |||
| 	log.Trace("AddTestPullRequestTask [head_repo_id: %d, head_branch: %s]: finding pull requests", repoID, branch) | ||||
| 	prs, err := GetUnmergedPullRequestsByHeadInfo(repoID, branch) | ||||
| 	if err != nil { | ||||
| 		log.Error(4, "Find pull requests [head_repo_id: %d, head_branch: %s]: %v", repoID, branch, err) | ||||
| 		log.Error("Find pull requests [head_repo_id: %d, head_branch: %s]: %v", repoID, branch, err) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	if isSync { | ||||
| 		requests := PullRequestList(prs) | ||||
| 		if err = requests.LoadAttributes(); err != nil { | ||||
| 			log.Error(4, "PullRequestList.LoadAttributes: %v", err) | ||||
| 			log.Error("PullRequestList.LoadAttributes: %v", err) | ||||
| 		} | ||||
| 		if invalidationErr := checkForInvalidation(requests, repoID, doer, branch); invalidationErr != nil { | ||||
| 			log.Error(4, "checkForInvalidation: %v", invalidationErr) | ||||
| 			log.Error("checkForInvalidation: %v", invalidationErr) | ||||
| 		} | ||||
| 		if err == nil { | ||||
| 			for _, pr := range prs { | ||||
| 				pr.Issue.PullRequest = pr | ||||
| 				if err = pr.Issue.LoadAttributes(); err != nil { | ||||
| 					log.Error(4, "LoadAttributes: %v", err) | ||||
| 					log.Error("LoadAttributes: %v", err) | ||||
| 					continue | ||||
| 				} | ||||
| 				if err = PrepareWebhooks(pr.Issue.Repo, HookEventPullRequest, &api.PullRequestPayload{ | ||||
|  | @ -1333,7 +1333,7 @@ func AddTestPullRequestTask(doer *User, repoID int64, branch string, isSync bool | |||
| 					Repository:  pr.Issue.Repo.APIFormat(AccessModeNone), | ||||
| 					Sender:      doer.APIFormat(), | ||||
| 				}); err != nil { | ||||
| 					log.Error(4, "PrepareWebhooks [pull_id: %v]: %v", pr.ID, err) | ||||
| 					log.Error("PrepareWebhooks [pull_id: %v]: %v", pr.ID, err) | ||||
| 					continue | ||||
| 				} | ||||
| 				go HookQueue.Add(pr.Issue.Repo.ID) | ||||
|  | @ -1347,7 +1347,7 @@ func AddTestPullRequestTask(doer *User, repoID int64, branch string, isSync bool | |||
| 	log.Trace("AddTestPullRequestTask [base_repo_id: %d, base_branch: %s]: finding pull requests", repoID, branch) | ||||
| 	prs, err = GetUnmergedPullRequestsByBaseInfo(repoID, branch) | ||||
| 	if err != nil { | ||||
| 		log.Error(4, "Find pull requests [base_repo_id: %d, base_branch: %s]: %v", repoID, branch, err) | ||||
| 		log.Error("Find pull requests [base_repo_id: %d, base_branch: %s]: %v", repoID, branch, err) | ||||
| 		return | ||||
| 	} | ||||
| 	for _, pr := range prs { | ||||
|  | @ -1367,7 +1367,7 @@ func checkForInvalidation(requests PullRequestList, repoID int64, doer *User, br | |||
| 	go func() { | ||||
| 		err := requests.InvalidateCodeComments(doer, gitRepo, branch) | ||||
| 		if err != nil { | ||||
| 			log.Error(4, "PullRequestList.InvalidateCodeComments: %v", err) | ||||
| 			log.Error("PullRequestList.InvalidateCodeComments: %v", err) | ||||
| 		} | ||||
| 	}() | ||||
| 	return nil | ||||
|  | @ -1396,7 +1396,7 @@ func (pr *PullRequest) checkAndUpdateStatus() { | |||
| 	// Make sure there is no waiting test to process before leaving the checking status.
 | ||||
| 	if !pullRequestQueue.Exist(pr.ID) { | ||||
| 		if err := pr.UpdateCols("status, conflicted_files"); err != nil { | ||||
| 			log.Error(4, "Update[%d]: %v", pr.ID, err) | ||||
| 			log.Error("Update[%d]: %v", pr.ID, err) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | @ -1404,7 +1404,7 @@ func (pr *PullRequest) checkAndUpdateStatus() { | |||
| // IsWorkInProgress determine if the Pull Request is a Work In Progress by its title
 | ||||
| func (pr *PullRequest) IsWorkInProgress() bool { | ||||
| 	if err := pr.LoadIssue(); err != nil { | ||||
| 		log.Error(4, "LoadIssue: %v", err) | ||||
| 		log.Error("LoadIssue: %v", err) | ||||
| 		return false | ||||
| 	} | ||||
| 
 | ||||
|  | @ -1425,7 +1425,7 @@ func (pr *PullRequest) IsFilesConflicted() bool { | |||
| // It returns an empty string when none were found
 | ||||
| func (pr *PullRequest) GetWorkInProgressPrefix() string { | ||||
| 	if err := pr.LoadIssue(); err != nil { | ||||
| 		log.Error(4, "LoadIssue: %v", err) | ||||
| 		log.Error("LoadIssue: %v", err) | ||||
| 		return "" | ||||
| 	} | ||||
| 
 | ||||
|  | @ -1444,7 +1444,7 @@ func TestPullRequests() { | |||
| 
 | ||||
| 	err := x.Where("status = ?", PullRequestStatusChecking).Find(&prs) | ||||
| 	if err != nil { | ||||
| 		log.Error(3, "Find Checking PRs", err) | ||||
| 		log.Error("Find Checking PRs: %v", err) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
|  | @ -1454,14 +1454,14 @@ func TestPullRequests() { | |||
| 	for _, pr := range prs { | ||||
| 		checkedPRs[pr.ID] = struct{}{} | ||||
| 		if err := pr.GetBaseRepo(); err != nil { | ||||
| 			log.Error(3, "GetBaseRepo: %v", err) | ||||
| 			log.Error("GetBaseRepo: %v", err) | ||||
| 			continue | ||||
| 		} | ||||
| 		if pr.manuallyMerged() { | ||||
| 			continue | ||||
| 		} | ||||
| 		if err := pr.testPatch(x); err != nil { | ||||
| 			log.Error(3, "testPatch: %v", err) | ||||
| 			log.Error("testPatch: %v", err) | ||||
| 			continue | ||||
| 		} | ||||
| 
 | ||||
|  | @ -1480,12 +1480,12 @@ func TestPullRequests() { | |||
| 
 | ||||
| 		pr, err := GetPullRequestByID(id) | ||||
| 		if err != nil { | ||||
| 			log.Error(4, "GetPullRequestByID[%s]: %v", prID, err) | ||||
| 			log.Error("GetPullRequestByID[%s]: %v", prID, err) | ||||
| 			continue | ||||
| 		} else if pr.manuallyMerged() { | ||||
| 			continue | ||||
| 		} else if err = pr.testPatch(x); err != nil { | ||||
| 			log.Error(4, "testPatch[%d]: %v", pr.ID, err) | ||||
| 			log.Error("testPatch[%d]: %v", pr.ID, err) | ||||
| 			continue | ||||
| 		} | ||||
| 
 | ||||
|  |  | |||
|  | @ -199,7 +199,7 @@ func CreateRelease(gitRepo *git.Repository, rel *Release, attachmentUUIDs []stri | |||
| 
 | ||||
| 	if !rel.IsDraft { | ||||
| 		if err := rel.LoadAttributes(); err != nil { | ||||
| 			log.Error(2, "LoadAttributes: %v", err) | ||||
| 			log.Error("LoadAttributes: %v", err) | ||||
| 		} else { | ||||
| 			mode, _ := AccessLevel(rel.Publisher, rel.Repo) | ||||
| 			if err := PrepareWebhooks(rel.Repo, HookEventRelease, &api.ReleasePayload{ | ||||
|  | @ -208,7 +208,7 @@ func CreateRelease(gitRepo *git.Repository, rel *Release, attachmentUUIDs []stri | |||
| 				Repository: rel.Repo.APIFormat(mode), | ||||
| 				Sender:     rel.Publisher.APIFormat(), | ||||
| 			}); err != nil { | ||||
| 				log.Error(2, "PrepareWebhooks: %v", err) | ||||
| 				log.Error("PrepareWebhooks: %v", err) | ||||
| 			} else { | ||||
| 				go HookQueue.Add(rel.Repo.ID) | ||||
| 			} | ||||
|  | @ -409,7 +409,7 @@ func UpdateRelease(doer *User, gitRepo *git.Repository, rel *Release, attachment | |||
| 		Repository: rel.Repo.APIFormat(mode), | ||||
| 		Sender:     rel.Publisher.APIFormat(), | ||||
| 	}); err1 != nil { | ||||
| 		log.Error(2, "PrepareWebhooks: %v", err) | ||||
| 		log.Error("PrepareWebhooks: %v", err) | ||||
| 	} else { | ||||
| 		go HookQueue.Add(rel.Repo.ID) | ||||
| 	} | ||||
|  | @ -464,7 +464,7 @@ func DeleteReleaseByID(id int64, u *User, delTag bool) error { | |||
| 		Repository: rel.Repo.APIFormat(mode), | ||||
| 		Sender:     rel.Publisher.APIFormat(), | ||||
| 	}); err != nil { | ||||
| 		log.Error(2, "PrepareWebhooks: %v", err) | ||||
| 		log.Error("PrepareWebhooks: %v", err) | ||||
| 	} else { | ||||
| 		go HookQueue.Add(rel.Repo.ID) | ||||
| 	} | ||||
|  |  | |||
|  | @ -74,13 +74,13 @@ func LoadRepoConfig() { | |||
| 	for i, t := range types { | ||||
| 		files, err := options.Dir(t) | ||||
| 		if err != nil { | ||||
| 			log.Fatal(4, "Failed to get %s files: %v", t, err) | ||||
| 			log.Fatal("Failed to get %s files: %v", t, err) | ||||
| 		} | ||||
| 		customPath := path.Join(setting.CustomPath, "options", t) | ||||
| 		if com.IsDir(customPath) { | ||||
| 			customFiles, err := com.StatDir(customPath) | ||||
| 			if err != nil { | ||||
| 				log.Fatal(4, "Failed to get custom %s files: %v", t, err) | ||||
| 				log.Fatal("Failed to get custom %s files: %v", t, err) | ||||
| 			} | ||||
| 
 | ||||
| 			for _, f := range customFiles { | ||||
|  | @ -122,19 +122,19 @@ func NewRepoContext() { | |||
| 
 | ||||
| 	// Check Git installation.
 | ||||
| 	if _, err := exec.LookPath("git"); err != nil { | ||||
| 		log.Fatal(4, "Failed to test 'git' command: %v (forgotten install?)", err) | ||||
| 		log.Fatal("Failed to test 'git' command: %v (forgotten install?)", err) | ||||
| 	} | ||||
| 
 | ||||
| 	// Check Git version.
 | ||||
| 	var err error | ||||
| 	setting.Git.Version, err = git.BinVersion() | ||||
| 	if err != nil { | ||||
| 		log.Fatal(4, "Failed to get Git version: %v", err) | ||||
| 		log.Fatal("Failed to get Git version: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	log.Info("Git Version: %s", setting.Git.Version) | ||||
| 	if version.Compare("1.7.1", setting.Git.Version, ">") { | ||||
| 		log.Fatal(4, "Gitea requires Git version greater or equal to 1.7.1") | ||||
| 		log.Fatal("Gitea requires Git version greater or equal to 1.7.1") | ||||
| 	} | ||||
| 
 | ||||
| 	// Git requires setting user.name and user.email in order to commit changes.
 | ||||
|  | @ -143,11 +143,11 @@ func NewRepoContext() { | |||
| 			// ExitError indicates this config is not set
 | ||||
| 			if _, ok := err.(*exec.ExitError); ok || strings.TrimSpace(stdout) == "" { | ||||
| 				if _, stderr, gerr := process.GetManager().Exec("NewRepoContext(set "+configKey+")", "git", "config", "--global", configKey, defaultValue); gerr != nil { | ||||
| 					log.Fatal(4, "Failed to set git %s(%s): %s", configKey, gerr, stderr) | ||||
| 					log.Fatal("Failed to set git %s(%s): %s", configKey, gerr, stderr) | ||||
| 				} | ||||
| 				log.Info("Git config %s set to %s", configKey, defaultValue) | ||||
| 			} else { | ||||
| 				log.Fatal(4, "Failed to get git %s(%s): %s", configKey, err, stderr) | ||||
| 				log.Fatal("Failed to get git %s(%s): %s", configKey, err, stderr) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | @ -155,7 +155,7 @@ func NewRepoContext() { | |||
| 	// Set git some configurations.
 | ||||
| 	if _, stderr, err := process.GetManager().Exec("NewRepoContext(git config --global core.quotepath false)", | ||||
| 		"git", "config", "--global", "core.quotepath", "false"); err != nil { | ||||
| 		log.Fatal(4, "Failed to execute 'git config --global core.quotepath false': %s", stderr) | ||||
| 		log.Fatal("Failed to execute 'git config --global core.quotepath false': %s", stderr) | ||||
| 	} | ||||
| 
 | ||||
| 	RemoveAllWithNotice("Clean up repository temporary data", filepath.Join(setting.AppDataPath, "tmp")) | ||||
|  | @ -281,7 +281,7 @@ func (repo *Repository) innerAPIFormat(e Engine, mode AccessMode, isParent bool) | |||
| 	if !isParent { | ||||
| 		err := repo.getBaseRepo(e) | ||||
| 		if err != nil { | ||||
| 			log.Error(4, "APIFormat: %v", err) | ||||
| 			log.Error("APIFormat: %v", err) | ||||
| 		} | ||||
| 		if repo.BaseRepo != nil { | ||||
| 			parent = repo.BaseRepo.innerAPIFormat(e, mode, true) | ||||
|  | @ -462,7 +462,7 @@ func (repo *Repository) GetOwnerName() error { | |||
| 
 | ||||
| func (repo *Repository) mustOwnerName(e Engine) string { | ||||
| 	if err := repo.getOwnerName(e); err != nil { | ||||
| 		log.Error(4, "Error loading repository owner name: %v", err) | ||||
| 		log.Error("Error loading repository owner name: %v", err) | ||||
| 		return "error" | ||||
| 	} | ||||
| 
 | ||||
|  | @ -724,7 +724,7 @@ var ( | |||
| func (repo *Repository) DescriptionHTML() template.HTML { | ||||
| 	desc, err := markup.RenderDescriptionHTML([]byte(repo.Description), repo.HTMLURL(), repo.ComposeMetas()) | ||||
| 	if err != nil { | ||||
| 		log.Error(4, "Failed to render description for %s (ID: %d): %v", repo.Name, repo.ID, err) | ||||
| 		log.Error("Failed to render description for %s (ID: %d): %v", repo.Name, repo.ID, err) | ||||
| 		return template.HTML(markup.Sanitize(repo.Description)) | ||||
| 	} | ||||
| 	return template.HTML(markup.Sanitize(string(desc))) | ||||
|  | @ -981,12 +981,12 @@ func MigrateRepository(doer, u *User, opts MigrateRepoOptions) (*Repository, err | |||
| 		} | ||||
| 
 | ||||
| 		if err = SyncReleasesWithTags(repo, gitRepo); err != nil { | ||||
| 			log.Error(4, "Failed to synchronize tags to releases for repository: %v", err) | ||||
| 			log.Error("Failed to synchronize tags to releases for repository: %v", err) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if err = repo.UpdateSize(); err != nil { | ||||
| 		log.Error(4, "Failed to update size for repository: %v", err) | ||||
| 		log.Error("Failed to update size for repository: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	if opts.IsMirror { | ||||
|  | @ -1405,7 +1405,7 @@ func CreateRepository(doer, u *User, opts CreateRepoOptions) (_ *Repository, err | |||
| 		repoPath := RepoPath(u.Name, repo.Name) | ||||
| 		if err = initRepository(sess, repoPath, u, repo, opts); err != nil { | ||||
| 			if err2 := os.RemoveAll(repoPath); err2 != nil { | ||||
| 				log.Error(4, "initRepository: %v", err) | ||||
| 				log.Error("initRepository: %v", err) | ||||
| 				return nil, fmt.Errorf( | ||||
| 					"delete repo directory %s/%s failed(2): %v", u.Name, repo.Name, err2) | ||||
| 			} | ||||
|  | @ -1435,7 +1435,7 @@ func countRepositories(userID int64, private bool) int64 { | |||
| 
 | ||||
| 	count, err := sess.Count(new(Repository)) | ||||
| 	if err != nil { | ||||
| 		log.Error(4, "countRepositories: %v", err) | ||||
| 		log.Error("countRepositories: %v", err) | ||||
| 	} | ||||
| 	return count | ||||
| } | ||||
|  | @ -1690,11 +1690,11 @@ func updateRepository(e Engine, repo *Repository, visibilityChanged bool) (err e | |||
| 		daemonExportFile := path.Join(repo.repoPath(e), `git-daemon-export-ok`) | ||||
| 		if repo.IsPrivate && com.IsExist(daemonExportFile) { | ||||
| 			if err = os.Remove(daemonExportFile); err != nil { | ||||
| 				log.Error(4, "Failed to remove %s: %v", daemonExportFile, err) | ||||
| 				log.Error("Failed to remove %s: %v", daemonExportFile, err) | ||||
| 			} | ||||
| 		} else if !repo.IsPrivate && !com.IsExist(daemonExportFile) { | ||||
| 			if f, err := os.Create(daemonExportFile); err != nil { | ||||
| 				log.Error(4, "Failed to create %s: %v", daemonExportFile, err) | ||||
| 				log.Error("Failed to create %s: %v", daemonExportFile, err) | ||||
| 			} else { | ||||
| 				f.Close() | ||||
| 			} | ||||
|  | @ -1712,7 +1712,7 @@ func updateRepository(e Engine, repo *Repository, visibilityChanged bool) (err e | |||
| 		} | ||||
| 
 | ||||
| 		if err = repo.updateSize(e); err != nil { | ||||
| 			log.Error(4, "Failed to update size for repository: %v", err) | ||||
| 			log.Error("Failed to update size for repository: %v", err) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
|  | @ -1928,7 +1928,7 @@ func DeleteRepository(doer *User, uid, repoID int64) error { | |||
| 
 | ||||
| 	if repo.NumForks > 0 { | ||||
| 		if _, err = sess.Exec("UPDATE `repository` SET fork_id=0,is_fork=? WHERE fork_id=?", false, repo.ID); err != nil { | ||||
| 			log.Error(4, "reset 'fork_id' and 'is_fork': %v", err) | ||||
| 			log.Error("reset 'fork_id' and 'is_fork': %v", err) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
|  | @ -2090,7 +2090,7 @@ func DeleteOldRepositoryArchives() { | |||
| 	log.Trace("Doing: ArchiveCleanup") | ||||
| 
 | ||||
| 	if err := x.Where("id > 0").Iterate(new(Repository), deleteOldRepositoryArchives); err != nil { | ||||
| 		log.Error(4, "ArchiveClean: %v", err) | ||||
| 		log.Error("ArchiveClean: %v", err) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | @ -2243,12 +2243,12 @@ func GitFsck() { | |||
| 					desc := fmt.Sprintf("Failed to health check repository (%s): %v", repoPath, err) | ||||
| 					log.Warn(desc) | ||||
| 					if err = CreateRepositoryNotice(desc); err != nil { | ||||
| 						log.Error(4, "CreateRepositoryNotice: %v", err) | ||||
| 						log.Error("CreateRepositoryNotice: %v", err) | ||||
| 					} | ||||
| 				} | ||||
| 				return nil | ||||
| 			}); err != nil { | ||||
| 		log.Error(4, "GitFsck: %v", err) | ||||
| 		log.Error("GitFsck: %v", err) | ||||
| 	} | ||||
| 	log.Trace("Finished: GitFsck") | ||||
| } | ||||
|  | @ -2283,7 +2283,7 @@ type repoChecker struct { | |||
| func repoStatsCheck(checker *repoChecker) { | ||||
| 	results, err := x.Query(checker.querySQL) | ||||
| 	if err != nil { | ||||
| 		log.Error(4, "Select %s: %v", checker.desc, err) | ||||
| 		log.Error("Select %s: %v", checker.desc, err) | ||||
| 		return | ||||
| 	} | ||||
| 	for _, result := range results { | ||||
|  | @ -2291,7 +2291,7 @@ func repoStatsCheck(checker *repoChecker) { | |||
| 		log.Trace("Updating %s: %d", checker.desc, id) | ||||
| 		_, err = x.Exec(checker.correctSQL, id, id) | ||||
| 		if err != nil { | ||||
| 			log.Error(4, "Update %s[%d]: %v", checker.desc, id, err) | ||||
| 			log.Error("Update %s[%d]: %v", checker.desc, id, err) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | @ -2345,14 +2345,14 @@ func CheckRepoStats() { | |||
| 	desc := "repository count 'num_closed_issues'" | ||||
| 	results, err := x.Query("SELECT repo.id FROM `repository` repo WHERE repo.num_closed_issues!=(SELECT COUNT(*) FROM `issue` WHERE repo_id=repo.id AND is_closed=? AND is_pull=?)", true, false) | ||||
| 	if err != nil { | ||||
| 		log.Error(4, "Select %s: %v", desc, err) | ||||
| 		log.Error("Select %s: %v", desc, err) | ||||
| 	} else { | ||||
| 		for _, result := range results { | ||||
| 			id := com.StrTo(result["id"]).MustInt64() | ||||
| 			log.Trace("Updating %s: %d", desc, id) | ||||
| 			_, err = x.Exec("UPDATE `repository` SET num_closed_issues=(SELECT COUNT(*) FROM `issue` WHERE repo_id=? AND is_closed=? AND is_pull=?) WHERE id=?", id, true, false, id) | ||||
| 			if err != nil { | ||||
| 				log.Error(4, "Update %s[%d]: %v", desc, id, err) | ||||
| 				log.Error("Update %s[%d]: %v", desc, id, err) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | @ -2362,7 +2362,7 @@ func CheckRepoStats() { | |||
| 	// ***** START: Repository.NumForks *****
 | ||||
| 	results, err = x.Query("SELECT repo.id FROM `repository` repo WHERE repo.num_forks!=(SELECT COUNT(*) FROM `repository` WHERE fork_id=repo.id)") | ||||
| 	if err != nil { | ||||
| 		log.Error(4, "Select repository count 'num_forks': %v", err) | ||||
| 		log.Error("Select repository count 'num_forks': %v", err) | ||||
| 	} else { | ||||
| 		for _, result := range results { | ||||
| 			id := com.StrTo(result["id"]).MustInt64() | ||||
|  | @ -2370,19 +2370,19 @@ func CheckRepoStats() { | |||
| 
 | ||||
| 			repo, err := GetRepositoryByID(id) | ||||
| 			if err != nil { | ||||
| 				log.Error(4, "GetRepositoryByID[%d]: %v", id, err) | ||||
| 				log.Error("GetRepositoryByID[%d]: %v", id, err) | ||||
| 				continue | ||||
| 			} | ||||
| 
 | ||||
| 			rawResult, err := x.Query("SELECT COUNT(*) FROM `repository` WHERE fork_id=?", repo.ID) | ||||
| 			if err != nil { | ||||
| 				log.Error(4, "Select count of forks[%d]: %v", repo.ID, err) | ||||
| 				log.Error("Select count of forks[%d]: %v", repo.ID, err) | ||||
| 				continue | ||||
| 			} | ||||
| 			repo.NumForks = int(parseCountResult(rawResult)) | ||||
| 
 | ||||
| 			if err = UpdateRepository(repo, false); err != nil { | ||||
| 				log.Error(4, "UpdateRepository[%d]: %v", id, err) | ||||
| 				log.Error("UpdateRepository[%d]: %v", id, err) | ||||
| 				continue | ||||
| 			} | ||||
| 		} | ||||
|  | @ -2485,13 +2485,13 @@ func ForkRepository(doer, u *User, oldRepo *Repository, name, desc string) (_ *R | |||
| 		Repo:   repo.APIFormat(mode), | ||||
| 		Sender: doer.APIFormat(), | ||||
| 	}); err != nil { | ||||
| 		log.Error(2, "PrepareWebhooks [repo_id: %d]: %v", oldRepo.ID, err) | ||||
| 		log.Error("PrepareWebhooks [repo_id: %d]: %v", oldRepo.ID, err) | ||||
| 	} else { | ||||
| 		go HookQueue.Add(oldRepo.ID) | ||||
| 	} | ||||
| 
 | ||||
| 	if err = repo.UpdateSize(); err != nil { | ||||
| 		log.Error(4, "Failed to update size for repository: %v", err) | ||||
| 		log.Error("Failed to update size for repository: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	// Copy LFS meta objects in new session
 | ||||
|  |  | |||
|  | @ -113,7 +113,7 @@ func populateRepoIndexer(maxRepoID int64) { | |||
| 			Limit(RepositoryListDefaultPageSize). | ||||
| 			Find(&repos) | ||||
| 		if err != nil { | ||||
| 			log.Error(4, "populateRepoIndexer: %v", err) | ||||
| 			log.Error("populateRepoIndexer: %v", err) | ||||
| 			return | ||||
| 		} else if len(repos) == 0 { | ||||
| 			break | ||||
|  | @ -314,11 +314,11 @@ func processRepoIndexerOperationQueue() { | |||
| 		op := <-repoIndexerOperationQueue | ||||
| 		if op.deleted { | ||||
| 			if err := indexer.DeleteRepoFromIndexer(op.repo.ID); err != nil { | ||||
| 				log.Error(4, "DeleteRepoFromIndexer: %v", err) | ||||
| 				log.Error("DeleteRepoFromIndexer: %v", err) | ||||
| 			} | ||||
| 		} else { | ||||
| 			if err := updateRepoIndexer(op.repo); err != nil { | ||||
| 				log.Error(4, "updateRepoIndexer: %v", err) | ||||
| 				log.Error("updateRepoIndexer: %v", err) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  |  | |||
|  | @ -57,7 +57,7 @@ func (m *Mirror) AfterLoad(session *xorm.Session) { | |||
| 	var err error | ||||
| 	m.Repo, err = getRepositoryByID(session, m.RepoID) | ||||
| 	if err != nil { | ||||
| 		log.Error(3, "getRepositoryByID[%d]: %v", m.ID, err) | ||||
| 		log.Error("getRepositoryByID[%d]: %v", m.ID, err) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | @ -85,7 +85,7 @@ func (m *Mirror) readAddress() { | |||
| 	var err error | ||||
| 	m.address, err = remoteAddress(m.Repo.RepoPath()) | ||||
| 	if err != nil { | ||||
| 		log.Error(4, "remoteAddress: %v", err) | ||||
| 		log.Error("remoteAddress: %v", err) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | @ -164,12 +164,12 @@ func parseRemoteUpdateOutput(output string) []*mirrorSyncResult { | |||
| 		case strings.HasPrefix(lines[i], "   "): // New commits of a reference
 | ||||
| 			delimIdx := strings.Index(lines[i][3:], " ") | ||||
| 			if delimIdx == -1 { | ||||
| 				log.Error(2, "SHA delimiter not found: %q", lines[i]) | ||||
| 				log.Error("SHA delimiter not found: %q", lines[i]) | ||||
| 				continue | ||||
| 			} | ||||
| 			shas := strings.Split(lines[i][3:delimIdx+3], "..") | ||||
| 			if len(shas) != 2 { | ||||
| 				log.Error(2, "Expect two SHAs but not what found: %q", lines[i]) | ||||
| 				log.Error("Expect two SHAs but not what found: %q", lines[i]) | ||||
| 				continue | ||||
| 			} | ||||
| 			results = append(results, &mirrorSyncResult{ | ||||
|  | @ -204,13 +204,13 @@ func (m *Mirror) runSync() ([]*mirrorSyncResult, bool) { | |||
| 		// contain a password
 | ||||
| 		message, err := sanitizeOutput(stderr, repoPath) | ||||
| 		if err != nil { | ||||
| 			log.Error(4, "sanitizeOutput: %v", err) | ||||
| 			log.Error("sanitizeOutput: %v", err) | ||||
| 			return nil, false | ||||
| 		} | ||||
| 		desc := fmt.Sprintf("Failed to update mirror repository '%s': %s", repoPath, message) | ||||
| 		log.Error(4, desc) | ||||
| 		log.Error(desc) | ||||
| 		if err = CreateRepositoryNotice(desc); err != nil { | ||||
| 			log.Error(4, "CreateRepositoryNotice: %v", err) | ||||
| 			log.Error("CreateRepositoryNotice: %v", err) | ||||
| 		} | ||||
| 		return nil, false | ||||
| 	} | ||||
|  | @ -218,15 +218,15 @@ func (m *Mirror) runSync() ([]*mirrorSyncResult, bool) { | |||
| 
 | ||||
| 	gitRepo, err := git.OpenRepository(repoPath) | ||||
| 	if err != nil { | ||||
| 		log.Error(4, "OpenRepository: %v", err) | ||||
| 		log.Error("OpenRepository: %v", err) | ||||
| 		return nil, false | ||||
| 	} | ||||
| 	if err = SyncReleasesWithTags(m.Repo, gitRepo); err != nil { | ||||
| 		log.Error(4, "Failed to synchronize tags to releases for repository: %v", err) | ||||
| 		log.Error("Failed to synchronize tags to releases for repository: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	if err := m.Repo.UpdateSize(); err != nil { | ||||
| 		log.Error(4, "Failed to update size for mirror repository: %v", err) | ||||
| 		log.Error("Failed to update size for mirror repository: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	if m.Repo.HasWiki() { | ||||
|  | @ -237,13 +237,13 @@ func (m *Mirror) runSync() ([]*mirrorSyncResult, bool) { | |||
| 			// contain a password
 | ||||
| 			message, err := sanitizeOutput(stderr, wikiPath) | ||||
| 			if err != nil { | ||||
| 				log.Error(4, "sanitizeOutput: %v", err) | ||||
| 				log.Error("sanitizeOutput: %v", err) | ||||
| 				return nil, false | ||||
| 			} | ||||
| 			desc := fmt.Sprintf("Failed to update mirror wiki repository '%s': %s", wikiPath, message) | ||||
| 			log.Error(4, desc) | ||||
| 			log.Error(desc) | ||||
| 			if err = CreateRepositoryNotice(desc); err != nil { | ||||
| 				log.Error(4, "CreateRepositoryNotice: %v", err) | ||||
| 				log.Error("CreateRepositoryNotice: %v", err) | ||||
| 			} | ||||
| 			return nil, false | ||||
| 		} | ||||
|  | @ -251,7 +251,7 @@ func (m *Mirror) runSync() ([]*mirrorSyncResult, bool) { | |||
| 
 | ||||
| 	branches, err := m.Repo.GetBranches() | ||||
| 	if err != nil { | ||||
| 		log.Error(4, "GetBranches: %v", err) | ||||
| 		log.Error("GetBranches: %v", err) | ||||
| 		return nil, false | ||||
| 	} | ||||
| 
 | ||||
|  | @ -310,14 +310,14 @@ func MirrorUpdate() { | |||
| 		Iterate(new(Mirror), func(idx int, bean interface{}) error { | ||||
| 			m := bean.(*Mirror) | ||||
| 			if m.Repo == nil { | ||||
| 				log.Error(4, "Disconnected mirror repository found: %d", m.ID) | ||||
| 				log.Error("Disconnected mirror repository found: %d", m.ID) | ||||
| 				return nil | ||||
| 			} | ||||
| 
 | ||||
| 			MirrorQueue.Add(m.RepoID) | ||||
| 			return nil | ||||
| 		}); err != nil { | ||||
| 		log.Error(4, "MirrorUpdate: %v", err) | ||||
| 		log.Error("MirrorUpdate: %v", err) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | @ -333,7 +333,7 @@ func SyncMirrors() { | |||
| 
 | ||||
| 		m, err := GetMirrorByRepoID(com.StrTo(repoID).MustInt64()) | ||||
| 		if err != nil { | ||||
| 			log.Error(4, "GetMirrorByRepoID [%s]: %v", repoID, err) | ||||
| 			log.Error("GetMirrorByRepoID [%s]: %v", repoID, err) | ||||
| 			continue | ||||
| 		} | ||||
| 
 | ||||
|  | @ -344,7 +344,7 @@ func SyncMirrors() { | |||
| 
 | ||||
| 		m.ScheduleNextUpdate() | ||||
| 		if err = updateMirror(sess, m); err != nil { | ||||
| 			log.Error(4, "UpdateMirror [%s]: %v", repoID, err) | ||||
| 			log.Error("UpdateMirror [%s]: %v", repoID, err) | ||||
| 			continue | ||||
| 		} | ||||
| 
 | ||||
|  | @ -354,7 +354,7 @@ func SyncMirrors() { | |||
| 		} else { | ||||
| 			gitRepo, err = git.OpenRepository(m.Repo.RepoPath()) | ||||
| 			if err != nil { | ||||
| 				log.Error(2, "OpenRepository [%d]: %v", m.RepoID, err) | ||||
| 				log.Error("OpenRepository [%d]: %v", m.RepoID, err) | ||||
| 				continue | ||||
| 			} | ||||
| 		} | ||||
|  | @ -368,7 +368,7 @@ func SyncMirrors() { | |||
| 			// Create reference
 | ||||
| 			if result.oldCommitID == gitShortEmptySha { | ||||
| 				if err = MirrorSyncCreateAction(m.Repo, result.refName); err != nil { | ||||
| 					log.Error(2, "MirrorSyncCreateAction [repo_id: %d]: %v", m.RepoID, err) | ||||
| 					log.Error("MirrorSyncCreateAction [repo_id: %d]: %v", m.RepoID, err) | ||||
| 				} | ||||
| 				continue | ||||
| 			} | ||||
|  | @ -376,7 +376,7 @@ func SyncMirrors() { | |||
| 			// Delete reference
 | ||||
| 			if result.newCommitID == gitShortEmptySha { | ||||
| 				if err = MirrorSyncDeleteAction(m.Repo, result.refName); err != nil { | ||||
| 					log.Error(2, "MirrorSyncDeleteAction [repo_id: %d]: %v", m.RepoID, err) | ||||
| 					log.Error("MirrorSyncDeleteAction [repo_id: %d]: %v", m.RepoID, err) | ||||
| 				} | ||||
| 				continue | ||||
| 			} | ||||
|  | @ -384,17 +384,17 @@ func SyncMirrors() { | |||
| 			// Push commits
 | ||||
| 			oldCommitID, err := git.GetFullCommitID(gitRepo.Path, result.oldCommitID) | ||||
| 			if err != nil { | ||||
| 				log.Error(2, "GetFullCommitID [%d]: %v", m.RepoID, err) | ||||
| 				log.Error("GetFullCommitID [%d]: %v", m.RepoID, err) | ||||
| 				continue | ||||
| 			} | ||||
| 			newCommitID, err := git.GetFullCommitID(gitRepo.Path, result.newCommitID) | ||||
| 			if err != nil { | ||||
| 				log.Error(2, "GetFullCommitID [%d]: %v", m.RepoID, err) | ||||
| 				log.Error("GetFullCommitID [%d]: %v", m.RepoID, err) | ||||
| 				continue | ||||
| 			} | ||||
| 			commits, err := gitRepo.CommitsBetweenIDs(newCommitID, oldCommitID) | ||||
| 			if err != nil { | ||||
| 				log.Error(2, "CommitsBetweenIDs [repo_id: %d, new_commit_id: %s, old_commit_id: %s]: %v", m.RepoID, newCommitID, oldCommitID, err) | ||||
| 				log.Error("CommitsBetweenIDs [repo_id: %d, new_commit_id: %s, old_commit_id: %s]: %v", m.RepoID, newCommitID, oldCommitID, err) | ||||
| 				continue | ||||
| 			} | ||||
| 			if err = MirrorSyncPushAction(m.Repo, MirrorSyncPushActionOptions{ | ||||
|  | @ -403,7 +403,7 @@ func SyncMirrors() { | |||
| 				NewCommitID: newCommitID, | ||||
| 				Commits:     ListToPushCommits(commits), | ||||
| 			}); err != nil { | ||||
| 				log.Error(2, "MirrorSyncPushAction [repo_id: %d]: %v", m.RepoID, err) | ||||
| 				log.Error("MirrorSyncPushAction [repo_id: %d]: %v", m.RepoID, err) | ||||
| 				continue | ||||
| 			} | ||||
| 		} | ||||
|  | @ -411,12 +411,12 @@ func SyncMirrors() { | |||
| 		// Get latest commit date and update to current repository updated time
 | ||||
| 		commitDate, err := git.GetLatestCommitTime(m.Repo.RepoPath()) | ||||
| 		if err != nil { | ||||
| 			log.Error(2, "GetLatestCommitDate [%s]: %v", m.RepoID, err) | ||||
| 			log.Error("GetLatestCommitDate [%d]: %v", m.RepoID, err) | ||||
| 			continue | ||||
| 		} | ||||
| 
 | ||||
| 		if _, err = sess.Exec("UPDATE repository SET updated_unix = ? WHERE id = ?", commitDate.Unix(), m.RepoID); err != nil { | ||||
| 			log.Error(2, "Update repository 'updated_unix' [%s]: %v", m.RepoID, err) | ||||
| 			log.Error("Update repository 'updated_unix' [%d]: %v", m.RepoID, err) | ||||
| 			continue | ||||
| 		} | ||||
| 	} | ||||
|  |  | |||
|  | @ -330,7 +330,7 @@ func appendAuthorizedKeysToFile(keys ...*PublicKey) error { | |||
| 
 | ||||
| 		// .ssh directory should have mode 700, and authorized_keys file should have mode 600.
 | ||||
| 		if fi.Mode().Perm() > 0600 { | ||||
| 			log.Error(4, "authorized_keys file has unusual permission flags: %s - setting to -rw-------", fi.Mode().Perm().String()) | ||||
| 			log.Error("authorized_keys file has unusual permission flags: %s - setting to -rw-------", fi.Mode().Perm().String()) | ||||
| 			if err = f.Chmod(0600); err != nil { | ||||
| 				return err | ||||
| 			} | ||||
|  |  | |||
|  | @ -281,7 +281,7 @@ func ParseCommitsWithStatus(oldCommits *list.List, repo *Repository) *list.List | |||
| 		} | ||||
| 		statuses, err := GetLatestCommitStatus(repo, commit.ID.String(), 0) | ||||
| 		if err != nil { | ||||
| 			log.Error(3, "GetLatestCommitStatus: %v", err) | ||||
| 			log.Error("GetLatestCommitStatus: %v", err) | ||||
| 		} else { | ||||
| 			commit.Status = CalcCommitStatus(statuses) | ||||
| 		} | ||||
|  |  | |||
|  | @ -52,7 +52,7 @@ func (list U2FRegistrationList) ToRegistrations() []u2f.Registration { | |||
| 	for _, reg := range list { | ||||
| 		r, err := reg.Parse() | ||||
| 		if err != nil { | ||||
| 			log.Fatal(4, "parsing u2f registration: %v", err) | ||||
| 			log.Fatal("parsing u2f registration: %v", err) | ||||
| 			continue | ||||
| 		} | ||||
| 		regs = append(regs, *r) | ||||
|  |  | |||
|  | @ -215,7 +215,7 @@ func pushUpdate(opts PushUpdateOptions) (repo *Repository, err error) { | |||
| 	} | ||||
| 
 | ||||
| 	if err = repo.UpdateSize(); err != nil { | ||||
| 		log.Error(4, "Failed to update size for repository: %v", err) | ||||
| 		log.Error("Failed to update size for repository: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	var commits = &PushCommits{} | ||||
|  |  | |||
|  | @ -367,7 +367,7 @@ func (u *User) SizedRelAvatarLink(size int) string { | |||
| 	case setting.DisableGravatar, setting.OfflineMode: | ||||
| 		if !com.IsFile(u.CustomAvatarPath()) { | ||||
| 			if err := u.GenerateRandomAvatar(); err != nil { | ||||
| 				log.Error(3, "GenerateRandomAvatar: %v", err) | ||||
| 				log.Error("GenerateRandomAvatar: %v", err) | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
|  | @ -523,7 +523,7 @@ func (u *User) IsOrganization() bool { | |||
| func (u *User) IsUserOrgOwner(orgID int64) bool { | ||||
| 	isOwner, err := IsOrganizationOwner(orgID, u.ID) | ||||
| 	if err != nil { | ||||
| 		log.Error(4, "IsOrganizationOwner: %v", err) | ||||
| 		log.Error("IsOrganizationOwner: %v", err) | ||||
| 		return false | ||||
| 	} | ||||
| 	return isOwner | ||||
|  | @ -533,7 +533,7 @@ func (u *User) IsUserOrgOwner(orgID int64) bool { | |||
| func (u *User) IsUserPartOfOrg(userID int64) bool { | ||||
| 	isMember, err := IsOrganizationMember(u.ID, userID) | ||||
| 	if err != nil { | ||||
| 		log.Error(4, "IsOrganizationMember: %v", err) | ||||
| 		log.Error("IsOrganizationMember: %v", err) | ||||
| 		return false | ||||
| 	} | ||||
| 	return isMember | ||||
|  | @ -543,7 +543,7 @@ func (u *User) IsUserPartOfOrg(userID int64) bool { | |||
| func (u *User) IsPublicMember(orgID int64) bool { | ||||
| 	isMember, err := IsPublicMembership(orgID, u.ID) | ||||
| 	if err != nil { | ||||
| 		log.Error(4, "IsPublicMembership: %v", err) | ||||
| 		log.Error("IsPublicMembership: %v", err) | ||||
| 		return false | ||||
| 	} | ||||
| 	return isMember | ||||
|  | @ -864,7 +864,7 @@ func getVerifyUser(code string) (user *User) { | |||
| 		if user, err = GetUserByName(string(b)); user != nil { | ||||
| 			return user | ||||
| 		} | ||||
| 		log.Error(4, "user.getVerifyUser: %v", err) | ||||
| 		log.Error("user.getVerifyUser: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
|  | @ -1490,11 +1490,11 @@ func deleteKeysMarkedForDeletion(keys []string) (bool, error) { | |||
| 	for _, KeyToDelete := range keys { | ||||
| 		key, err := searchPublicKeyByContentWithEngine(sess, KeyToDelete) | ||||
| 		if err != nil { | ||||
| 			log.Error(4, "SearchPublicKeyByContent: %v", err) | ||||
| 			log.Error("SearchPublicKeyByContent: %v", err) | ||||
| 			continue | ||||
| 		} | ||||
| 		if err = deletePublicKeys(sess, key.ID); err != nil { | ||||
| 			log.Error(4, "deletePublicKeys: %v", err) | ||||
| 			log.Error("deletePublicKeys: %v", err) | ||||
| 			continue | ||||
| 		} | ||||
| 		sshKeysNeedUpdate = true | ||||
|  | @ -1515,7 +1515,7 @@ func addLdapSSHPublicKeys(usr *User, s *LoginSource, SSHPublicKeys []string) boo | |||
| 		if err == nil { | ||||
| 			sshKeyName := fmt.Sprintf("%s-%s", s.Name, sshKey[0:40]) | ||||
| 			if _, err := AddPublicKey(usr.ID, sshKeyName, sshKey, s.ID); err != nil { | ||||
| 				log.Error(4, "addLdapSSHPublicKeys[%s]: Error adding LDAP Public SSH Key for user %s: %v", s.Name, usr.Name, err) | ||||
| 				log.Error("addLdapSSHPublicKeys[%s]: Error adding LDAP Public SSH Key for user %s: %v", s.Name, usr.Name, err) | ||||
| 			} else { | ||||
| 				log.Trace("addLdapSSHPublicKeys[%s]: Added LDAP Public SSH Key for user %s", s.Name, usr.Name) | ||||
| 				sshKeysNeedUpdate = true | ||||
|  | @ -1537,7 +1537,7 @@ func synchronizeLdapSSHPublicKeys(usr *User, s *LoginSource, SSHPublicKeys []str | |||
| 	var giteaKeys []string | ||||
| 	keys, err := ListPublicLdapSSHKeys(usr.ID, s.ID) | ||||
| 	if err != nil { | ||||
| 		log.Error(4, "synchronizeLdapSSHPublicKeys[%s]: Error listing LDAP Public SSH Keys for user %s: %v", s.Name, usr.Name, err) | ||||
| 		log.Error("synchronizeLdapSSHPublicKeys[%s]: Error listing LDAP Public SSH Keys for user %s: %v", s.Name, usr.Name, err) | ||||
| 	} | ||||
| 
 | ||||
| 	for _, v := range keys { | ||||
|  | @ -1586,7 +1586,7 @@ func synchronizeLdapSSHPublicKeys(usr *User, s *LoginSource, SSHPublicKeys []str | |||
| 	// Delete LDAP keys from DB that doesn't exist in LDAP
 | ||||
| 	needUpd, err := deleteKeysMarkedForDeletion(giteaKeysToDelete) | ||||
| 	if err != nil { | ||||
| 		log.Error(4, "synchronizeLdapSSHPublicKeys[%s]: Error deleting LDAP Public SSH Keys marked for deletion for user %s: %v", s.Name, usr.Name, err) | ||||
| 		log.Error("synchronizeLdapSSHPublicKeys[%s]: Error deleting LDAP Public SSH Keys marked for deletion for user %s: %v", s.Name, usr.Name, err) | ||||
| 	} | ||||
| 	if needUpd { | ||||
| 		sshKeysNeedUpdate = true | ||||
|  | @ -1606,7 +1606,7 @@ func SyncExternalUsers() { | |||
| 
 | ||||
| 	ls, err := LoginSources() | ||||
| 	if err != nil { | ||||
| 		log.Error(4, "SyncExternalUsers: %v", err) | ||||
| 		log.Error("SyncExternalUsers: %v", err) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
|  | @ -1669,7 +1669,7 @@ func SyncExternalUsers() { | |||
| 					err = CreateUser(usr) | ||||
| 
 | ||||
| 					if err != nil { | ||||
| 						log.Error(4, "SyncExternalUsers[%s]: Error creating user %s: %v", s.Name, su.Username, err) | ||||
| 						log.Error("SyncExternalUsers[%s]: Error creating user %s: %v", s.Name, su.Username, err) | ||||
| 					} else if isAttributeSSHPublicKeySet { | ||||
| 						log.Trace("SyncExternalUsers[%s]: Adding LDAP Public SSH Keys for user %s", s.Name, usr.Name) | ||||
| 						if addLdapSSHPublicKeys(usr, s, su.SSHPublicKey) { | ||||
|  | @ -1702,7 +1702,7 @@ func SyncExternalUsers() { | |||
| 
 | ||||
| 						err = UpdateUserCols(usr, "full_name", "email", "is_admin", "is_active") | ||||
| 						if err != nil { | ||||
| 							log.Error(4, "SyncExternalUsers[%s]: Error updating user %s: %v", s.Name, usr.Name, err) | ||||
| 							log.Error("SyncExternalUsers[%s]: Error updating user %s: %v", s.Name, usr.Name, err) | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
|  | @ -1729,7 +1729,7 @@ func SyncExternalUsers() { | |||
| 						usr.IsActive = false | ||||
| 						err = UpdateUserCols(&usr, "is_active") | ||||
| 						if err != nil { | ||||
| 							log.Error(4, "SyncExternalUsers[%s]: Error deactivating user %s: %v", s.Name, usr.Name, err) | ||||
| 							log.Error("SyncExternalUsers[%s]: Error deactivating user %s: %v", s.Name, usr.Name, err) | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
|  |  | |||
|  | @ -123,7 +123,7 @@ type Webhook struct { | |||
| func (w *Webhook) AfterLoad() { | ||||
| 	w.HookEvent = &HookEvent{} | ||||
| 	if err := json.Unmarshal([]byte(w.Events), w.HookEvent); err != nil { | ||||
| 		log.Error(3, "Unmarshal[%d]: %v", w.ID, err) | ||||
| 		log.Error("Unmarshal[%d]: %v", w.ID, err) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | @ -131,7 +131,7 @@ func (w *Webhook) AfterLoad() { | |||
| func (w *Webhook) GetSlackHook() *SlackMeta { | ||||
| 	s := &SlackMeta{} | ||||
| 	if err := json.Unmarshal([]byte(w.Meta), s); err != nil { | ||||
| 		log.Error(4, "webhook.GetSlackHook(%d): %v", w.ID, err) | ||||
| 		log.Error("webhook.GetSlackHook(%d): %v", w.ID, err) | ||||
| 	} | ||||
| 	return s | ||||
| } | ||||
|  | @ -140,7 +140,7 @@ func (w *Webhook) GetSlackHook() *SlackMeta { | |||
| func (w *Webhook) GetDiscordHook() *DiscordMeta { | ||||
| 	s := &DiscordMeta{} | ||||
| 	if err := json.Unmarshal([]byte(w.Meta), s); err != nil { | ||||
| 		log.Error(4, "webhook.GetDiscordHook(%d): %v", w.ID, err) | ||||
| 		log.Error("webhook.GetDiscordHook(%d): %v", w.ID, err) | ||||
| 	} | ||||
| 	return s | ||||
| } | ||||
|  | @ -572,13 +572,13 @@ func (t *HookTask) AfterLoad() { | |||
| 
 | ||||
| 	t.RequestInfo = &HookRequest{} | ||||
| 	if err := json.Unmarshal([]byte(t.RequestContent), t.RequestInfo); err != nil { | ||||
| 		log.Error(3, "Unmarshal RequestContent[%d]: %v", t.ID, err) | ||||
| 		log.Error("Unmarshal RequestContent[%d]: %v", t.ID, err) | ||||
| 	} | ||||
| 
 | ||||
| 	if len(t.ResponseContent) > 0 { | ||||
| 		t.ResponseInfo = &HookResponse{} | ||||
| 		if err := json.Unmarshal([]byte(t.ResponseContent), t.ResponseInfo); err != nil { | ||||
| 			log.Error(3, "Unmarshal ResponseContent[%d]: %v", t.ID, err) | ||||
| 			log.Error("Unmarshal ResponseContent[%d]: %v", t.ID, err) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | @ -586,7 +586,7 @@ func (t *HookTask) AfterLoad() { | |||
| func (t *HookTask) simpleMarshalJSON(v interface{}) string { | ||||
| 	p, err := json.Marshal(v) | ||||
| 	if err != nil { | ||||
| 		log.Error(3, "Marshal [%d]: %v", t.ID, err) | ||||
| 		log.Error("Marshal [%d]: %v", t.ID, err) | ||||
| 	} | ||||
| 	return string(p) | ||||
| } | ||||
|  | @ -666,7 +666,7 @@ func prepareWebhook(e Engine, w *Webhook, repo *Repository, event HookEventType, | |||
| 	if len(w.Secret) > 0 { | ||||
| 		data, err := payloader.JSONPayload() | ||||
| 		if err != nil { | ||||
| 			log.Error(2, "prepareWebhooks.JSONPayload: %v", err) | ||||
| 			log.Error("prepareWebhooks.JSONPayload: %v", err) | ||||
| 		} | ||||
| 		sig := hmac.New(sha256.New, []byte(w.Secret)) | ||||
| 		sig.Write(data) | ||||
|  | @ -765,13 +765,13 @@ func (t *HookTask) deliver() { | |||
| 		} | ||||
| 
 | ||||
| 		if err := UpdateHookTask(t); err != nil { | ||||
| 			log.Error(4, "UpdateHookTask [%d]: %v", t.ID, err) | ||||
| 			log.Error("UpdateHookTask [%d]: %v", t.ID, err) | ||||
| 		} | ||||
| 
 | ||||
| 		// Update webhook last delivery status.
 | ||||
| 		w, err := GetWebhookByID(t.HookID) | ||||
| 		if err != nil { | ||||
| 			log.Error(5, "GetWebhookByID: %v", err) | ||||
| 			log.Error("GetWebhookByID: %v", err) | ||||
| 			return | ||||
| 		} | ||||
| 		if t.IsSucceed { | ||||
|  | @ -780,7 +780,7 @@ func (t *HookTask) deliver() { | |||
| 			w.LastStatus = HookStatusFail | ||||
| 		} | ||||
| 		if err = UpdateWebhookLastStatus(w); err != nil { | ||||
| 			log.Error(5, "UpdateWebhookLastStatus: %v", err) | ||||
| 			log.Error("UpdateWebhookLastStatus: %v", err) | ||||
| 			return | ||||
| 		} | ||||
| 	}() | ||||
|  | @ -813,7 +813,7 @@ func DeliverHooks() { | |||
| 	tasks := make([]*HookTask, 0, 10) | ||||
| 	err := x.Where("is_delivered=?", false).Find(&tasks) | ||||
| 	if err != nil { | ||||
| 		log.Error(4, "DeliverHooks: %v", err) | ||||
| 		log.Error("DeliverHooks: %v", err) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
|  | @ -829,13 +829,13 @@ func DeliverHooks() { | |||
| 
 | ||||
| 		repoID, err := com.StrTo(repoIDStr).Int64() | ||||
| 		if err != nil { | ||||
| 			log.Error(4, "Invalid repo ID: %s", repoIDStr) | ||||
| 			log.Error("Invalid repo ID: %s", repoIDStr) | ||||
| 			continue | ||||
| 		} | ||||
| 
 | ||||
| 		tasks = make([]*HookTask, 0, 5) | ||||
| 		if err := x.Where("repo_id=? AND is_delivered=?", repoID, false).Find(&tasks); err != nil { | ||||
| 			log.Error(4, "Get repository [%s] hook tasks: %v", repoID, err) | ||||
| 			log.Error("Get repository [%d] hook tasks: %v", repoID, err) | ||||
| 			continue | ||||
| 		} | ||||
| 		for _, t := range tasks { | ||||
|  |  | |||
|  | @ -63,13 +63,13 @@ func SignedInID(ctx *macaron.Context, sess session.Store) int64 { | |||
| 			t, err := models.GetAccessTokenBySHA(tokenSHA) | ||||
| 			if err != nil { | ||||
| 				if models.IsErrAccessTokenNotExist(err) || models.IsErrAccessTokenEmpty(err) { | ||||
| 					log.Error(4, "GetAccessTokenBySHA: %v", err) | ||||
| 					log.Error("GetAccessTokenBySHA: %v", err) | ||||
| 				} | ||||
| 				return 0 | ||||
| 			} | ||||
| 			t.UpdatedUnix = util.TimeStampNow() | ||||
| 			if err = models.UpdateAccessToken(t); err != nil { | ||||
| 				log.Error(4, "UpdateAccessToken: %v", err) | ||||
| 				log.Error("UpdateAccessToken: %v", err) | ||||
| 			} | ||||
| 			ctx.Data["IsApiToken"] = true | ||||
| 			return t.UID | ||||
|  | @ -92,7 +92,7 @@ func checkOAuthAccessToken(accessToken string) int64 { | |||
| 	} | ||||
| 	token, err := models.ParseOAuth2Token(accessToken) | ||||
| 	if err != nil { | ||||
| 		log.Trace("ParseOAuth2Token", err) | ||||
| 		log.Trace("ParseOAuth2Token: %v", err) | ||||
| 		return 0 | ||||
| 	} | ||||
| 	var grant *models.OAuth2Grant | ||||
|  | @ -120,7 +120,7 @@ func SignedInUser(ctx *macaron.Context, sess session.Store) (*models.User, bool) | |||
| 		if err == nil { | ||||
| 			return user, false | ||||
| 		} else if !models.IsErrUserNotExist(err) { | ||||
| 			log.Error(4, "GetUserById: %v", err) | ||||
| 			log.Error("GetUserById: %v", err) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
|  | @ -130,7 +130,7 @@ func SignedInUser(ctx *macaron.Context, sess session.Store) (*models.User, bool) | |||
| 			u, err := models.GetUserByName(webAuthUser) | ||||
| 			if err != nil { | ||||
| 				if !models.IsErrUserNotExist(err) { | ||||
| 					log.Error(4, "GetUserByName: %v", err) | ||||
| 					log.Error("GetUserByName: %v", err) | ||||
| 					return nil, false | ||||
| 				} | ||||
| 
 | ||||
|  | @ -151,7 +151,7 @@ func SignedInUser(ctx *macaron.Context, sess session.Store) (*models.User, bool) | |||
| 					} | ||||
| 					if err = models.CreateUser(u); err != nil { | ||||
| 						// FIXME: should I create a system notice?
 | ||||
| 						log.Error(4, "CreateUser: %v", err) | ||||
| 						log.Error("CreateUser: %v", err) | ||||
| 						return nil, false | ||||
| 					} | ||||
| 					return u, false | ||||
|  | @ -183,13 +183,13 @@ func SignedInUser(ctx *macaron.Context, sess session.Store) (*models.User, bool) | |||
| 				if isUsernameToken { | ||||
| 					u, err = models.GetUserByID(token.UID) | ||||
| 					if err != nil { | ||||
| 						log.Error(4, "GetUserByID:  %v", err) | ||||
| 						log.Error("GetUserByID:  %v", err) | ||||
| 						return nil, false | ||||
| 					} | ||||
| 				} else { | ||||
| 					u, err = models.GetUserByName(uname) | ||||
| 					if err != nil { | ||||
| 						log.Error(4, "GetUserByID:  %v", err) | ||||
| 						log.Error("GetUserByID:  %v", err) | ||||
| 						return nil, false | ||||
| 					} | ||||
| 					if u.ID != token.UID { | ||||
|  | @ -198,11 +198,11 @@ func SignedInUser(ctx *macaron.Context, sess session.Store) (*models.User, bool) | |||
| 				} | ||||
| 				token.UpdatedUnix = util.TimeStampNow() | ||||
| 				if err = models.UpdateAccessToken(token); err != nil { | ||||
| 					log.Error(4, "UpdateAccessToken:  %v", err) | ||||
| 					log.Error("UpdateAccessToken:  %v", err) | ||||
| 				} | ||||
| 			} else { | ||||
| 				if !models.IsErrAccessTokenNotExist(err) && !models.IsErrAccessTokenEmpty(err) { | ||||
| 					log.Error(4, "GetAccessTokenBySha: %v", err) | ||||
| 					log.Error("GetAccessTokenBySha: %v", err) | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
|  | @ -210,7 +210,7 @@ func SignedInUser(ctx *macaron.Context, sess session.Store) (*models.User, bool) | |||
| 				u, err = models.UserSignIn(uname, passwd) | ||||
| 				if err != nil { | ||||
| 					if !models.IsErrUserNotExist(err) { | ||||
| 						log.Error(4, "UserSignIn: %v", err) | ||||
| 						log.Error("UserSignIn: %v", err) | ||||
| 					} | ||||
| 					return nil, false | ||||
| 				} | ||||
|  |  | |||
|  | @ -107,7 +107,7 @@ func (ls *Source) findUserDN(l *ldap.Conn, name string) (string, bool) { | |||
| 
 | ||||
| 	userDN := sr.Entries[0].DN | ||||
| 	if userDN == "" { | ||||
| 		log.Error(4, "LDAP search was successful, but found no DN!") | ||||
| 		log.Error("LDAP search was successful, but found no DN!") | ||||
| 		return "", false | ||||
| 	} | ||||
| 
 | ||||
|  | @ -162,7 +162,7 @@ func checkAdmin(l *ldap.Conn, ls *Source, userDN string) bool { | |||
| 		sr, err := l.Search(search) | ||||
| 
 | ||||
| 		if err != nil { | ||||
| 			log.Error(4, "LDAP Admin Search failed unexpectedly! (%v)", err) | ||||
| 			log.Error("LDAP Admin Search failed unexpectedly! (%v)", err) | ||||
| 		} else if len(sr.Entries) < 1 { | ||||
| 			log.Trace("LDAP Admin Search found no matching entries.") | ||||
| 		} else { | ||||
|  | @ -176,12 +176,12 @@ func checkAdmin(l *ldap.Conn, ls *Source, userDN string) bool { | |||
| func (ls *Source) SearchEntry(name, passwd string, directBind bool) *SearchResult { | ||||
| 	// See https://tools.ietf.org/search/rfc4513#section-5.1.2
 | ||||
| 	if len(passwd) == 0 { | ||||
| 		log.Debug("Auth. failed for %s, password cannot be empty") | ||||
| 		log.Debug("Auth. failed for %s, password cannot be empty", name) | ||||
| 		return nil | ||||
| 	} | ||||
| 	l, err := dial(ls) | ||||
| 	if err != nil { | ||||
| 		log.Error(4, "LDAP Connect error, %s:%v", ls.Host, err) | ||||
| 		log.Error("LDAP Connect error, %s:%v", ls.Host, err) | ||||
| 		ls.Enabled = false | ||||
| 		return nil | ||||
| 	} | ||||
|  | @ -261,7 +261,7 @@ func (ls *Source) SearchEntry(name, passwd string, directBind bool) *SearchResul | |||
| 
 | ||||
| 	sr, err := l.Search(search) | ||||
| 	if err != nil { | ||||
| 		log.Error(4, "LDAP Search failed unexpectedly! (%v)", err) | ||||
| 		log.Error("LDAP Search failed unexpectedly! (%v)", err) | ||||
| 		return nil | ||||
| 	} else if len(sr.Entries) < 1 { | ||||
| 		if directBind { | ||||
|  | @ -311,7 +311,7 @@ func (ls *Source) UsePagedSearch() bool { | |||
| func (ls *Source) SearchEntries() []*SearchResult { | ||||
| 	l, err := dial(ls) | ||||
| 	if err != nil { | ||||
| 		log.Error(4, "LDAP Connect error, %s:%v", ls.Host, err) | ||||
| 		log.Error("LDAP Connect error, %s:%v", ls.Host, err) | ||||
| 		ls.Enabled = false | ||||
| 		return nil | ||||
| 	} | ||||
|  | @ -349,7 +349,7 @@ func (ls *Source) SearchEntries() []*SearchResult { | |||
| 		sr, err = l.Search(search) | ||||
| 	} | ||||
| 	if err != nil { | ||||
| 		log.Error(4, "LDAP Search failed unexpectedly! (%v)", err) | ||||
| 		log.Error("LDAP Search failed unexpectedly! (%v)", err) | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
|  |  | |||
|  | @ -110,7 +110,7 @@ func GetRandomBytesAsBase64(n int) string { | |||
| 	_, err := io.ReadFull(rand.Reader, bytes) | ||||
| 
 | ||||
| 	if err != nil { | ||||
| 		log.Fatal(4, "Error reading random bytes: %v", err) | ||||
| 		log.Fatal("Error reading random bytes: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	return base64.RawURLEncoding.EncodeToString(bytes) | ||||
|  | @ -197,12 +197,12 @@ const DefaultAvatarSize = -1 | |||
| func libravatarURL(email string) (*url.URL, error) { | ||||
| 	urlStr, err := setting.LibravatarService.FromEmail(email) | ||||
| 	if err != nil { | ||||
| 		log.Error(4, "LibravatarService.FromEmail(email=%s): error %v", email, err) | ||||
| 		log.Error("LibravatarService.FromEmail(email=%s): error %v", email, err) | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	u, err := url.Parse(urlStr) | ||||
| 	if err != nil { | ||||
| 		log.Error(4, "Failed to parse libravatar url(%s): error %v", urlStr, err) | ||||
| 		log.Error("Failed to parse libravatar url(%s): error %v", urlStr, err) | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return u, nil | ||||
|  |  | |||
|  | @ -72,7 +72,7 @@ func (ctx *APIContext) Error(status int, title string, obj interface{}) { | |||
| 	} | ||||
| 
 | ||||
| 	if status == 500 { | ||||
| 		log.Error(4, "%s: %s", title, message) | ||||
| 		log.Error("%s: %s", title, message) | ||||
| 	} | ||||
| 
 | ||||
| 	ctx.JSON(status, APIError{ | ||||
|  |  | |||
|  | @ -116,7 +116,7 @@ func (ctx *Context) RenderWithErr(msg string, tpl base.TplName, form interface{} | |||
| // NotFound displays a 404 (Not Found) page and prints the given error, if any.
 | ||||
| func (ctx *Context) NotFound(title string, err error) { | ||||
| 	if err != nil { | ||||
| 		log.Error(4, "%s: %v", title, err) | ||||
| 		log.Error("%s: %v", title, err) | ||||
| 		if macaron.Env != macaron.PROD { | ||||
| 			ctx.Data["ErrorMsg"] = err | ||||
| 		} | ||||
|  | @ -131,7 +131,7 @@ func (ctx *Context) NotFound(title string, err error) { | |||
| // error, if any.
 | ||||
| func (ctx *Context) ServerError(title string, err error) { | ||||
| 	if err != nil { | ||||
| 		log.Error(4, "%s: %v", title, err) | ||||
| 		log.Error("%s: %v", title, err) | ||||
| 		if macaron.Env != macaron.PROD { | ||||
| 			ctx.Data["ErrorMsg"] = err | ||||
| 		} | ||||
|  | @ -156,7 +156,7 @@ func (ctx *Context) NotFoundOrServerError(title string, errck func(error) bool, | |||
| // HandleText handles HTTP status code
 | ||||
| func (ctx *Context) HandleText(status int, title string) { | ||||
| 	if (status/100 == 4) || (status/100 == 5) { | ||||
| 		log.Error(4, "%s", title) | ||||
| 		log.Error("%s", title) | ||||
| 	} | ||||
| 	ctx.PlainText(status, []byte(title)) | ||||
| } | ||||
|  |  | |||
|  | @ -17,11 +17,9 @@ | |||
| package context | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"fmt" | ||||
| 	"io/ioutil" | ||||
| 	"runtime" | ||||
| 
 | ||||
| 	"code.gitea.io/gitea/modules/log" | ||||
| 	macaron "gopkg.in/macaron.v1" | ||||
| ) | ||||
| 
 | ||||
|  | @ -32,7 +30,7 @@ func Recovery() macaron.Handler { | |||
| 	return func(ctx *Context) { | ||||
| 		defer func() { | ||||
| 			if err := recover(); err != nil { | ||||
| 				combinedErr := fmt.Errorf("%s\n%s", err, string(stack(3))) | ||||
| 				combinedErr := fmt.Errorf("%s\n%s", err, string(log.Stack(2))) | ||||
| 				ctx.ServerError("PANIC:", combinedErr) | ||||
| 			} | ||||
| 		}() | ||||
|  | @ -40,73 +38,3 @@ func Recovery() macaron.Handler { | |||
| 		ctx.Next() | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| var ( | ||||
| 	unknown = []byte("???") | ||||
| ) | ||||
| 
 | ||||
| // Although we could just use debug.Stack(), this routine will return the source code
 | ||||
| // skip the provided number of frames - i.e. allowing us to ignore this function call
 | ||||
| // and the preceding function call.
 | ||||
| // If the problem is a lack of memory of course all this is not going to work...
 | ||||
| func stack(skip int) []byte { | ||||
| 	buf := new(bytes.Buffer) | ||||
| 
 | ||||
| 	// Store the last file we opened as its probable that the preceding stack frame
 | ||||
| 	// will be in the same file
 | ||||
| 	var lines [][]byte | ||||
| 	var lastFilename string | ||||
| 	for i := skip; ; i++ { // Skip over frames
 | ||||
| 		programCounter, filename, lineNumber, ok := runtime.Caller(i) | ||||
| 		// If we can't retrieve the information break - basically we're into go internals at this point.
 | ||||
| 		if !ok { | ||||
| 			break | ||||
| 		} | ||||
| 
 | ||||
| 		// Print equivalent of debug.Stack()
 | ||||
| 		fmt.Fprintf(buf, "%s:%d (0x%x)\n", filename, lineNumber, programCounter) | ||||
| 		// Now try to print the offending line
 | ||||
| 		if filename != lastFilename { | ||||
| 			data, err := ioutil.ReadFile(filename) | ||||
| 			if err != nil { | ||||
| 				// can't read this sourcefile
 | ||||
| 				// likely we don't have the sourcecode available
 | ||||
| 				continue | ||||
| 			} | ||||
| 			lines = bytes.Split(data, []byte{'\n'}) | ||||
| 			lastFilename = filename | ||||
| 		} | ||||
| 		fmt.Fprintf(buf, "\t%s: %s\n", functionName(programCounter), source(lines, lineNumber)) | ||||
| 	} | ||||
| 	return buf.Bytes() | ||||
| } | ||||
| 
 | ||||
| // functionName converts the provided programCounter into a function name
 | ||||
| func functionName(programCounter uintptr) []byte { | ||||
| 	function := runtime.FuncForPC(programCounter) | ||||
| 	if function == nil { | ||||
| 		return unknown | ||||
| 	} | ||||
| 	name := []byte(function.Name()) | ||||
| 
 | ||||
| 	// Because we provide the filename we can drop the preceding package name.
 | ||||
| 	if lastslash := bytes.LastIndex(name, []byte("/")); lastslash >= 0 { | ||||
| 		name = name[lastslash+1:] | ||||
| 	} | ||||
| 	// And the current package name.
 | ||||
| 	if period := bytes.Index(name, []byte(".")); period >= 0 { | ||||
| 		name = name[period+1:] | ||||
| 	} | ||||
| 	// And we should just replace the interpunct with a dot
 | ||||
| 	name = bytes.Replace(name, []byte("·"), []byte("."), -1) | ||||
| 	return name | ||||
| } | ||||
| 
 | ||||
| // source returns a space-trimmed slice of the n'th line.
 | ||||
| func source(lines [][]byte, n int) []byte { | ||||
| 	n-- // in stack trace, lines are 1-indexed but our array is 0-indexed
 | ||||
| 	if n < 0 || n >= len(lines) { | ||||
| 		return unknown | ||||
| 	} | ||||
| 	return bytes.TrimSpace(lines[n]) | ||||
| } | ||||
|  |  | |||
|  | @ -124,7 +124,7 @@ func (r *Repository) BranchNameSubURL() string { | |||
| 	case r.IsViewCommit: | ||||
| 		return "commit/" + r.BranchName | ||||
| 	} | ||||
| 	log.Error(4, "Unknown view type for repo: %v", r) | ||||
| 	log.Error("Unknown view type for repo: %v", r) | ||||
| 	return "" | ||||
| } | ||||
| 
 | ||||
|  | @ -536,7 +536,7 @@ func getRefName(ctx *Context, pathType RepoRefType) string { | |||
| 		} | ||||
| 		return path | ||||
| 	default: | ||||
| 		log.Error(4, "Unrecognized path type: %v", path) | ||||
| 		log.Error("Unrecognized path type: %v", path) | ||||
| 	} | ||||
| 	return "" | ||||
| } | ||||
|  |  | |||
|  | @ -25,7 +25,7 @@ func NewContext() { | |||
| 	if setting.Cron.UpdateMirror.Enabled { | ||||
| 		entry, err = c.AddFunc("Update mirrors", setting.Cron.UpdateMirror.Schedule, models.MirrorUpdate) | ||||
| 		if err != nil { | ||||
| 			log.Fatal(4, "Cron[Update mirrors]: %v", err) | ||||
| 			log.Fatal("Cron[Update mirrors]: %v", err) | ||||
| 		} | ||||
| 		if setting.Cron.UpdateMirror.RunAtStart { | ||||
| 			entry.Prev = time.Now() | ||||
|  | @ -36,7 +36,7 @@ func NewContext() { | |||
| 	if setting.Cron.RepoHealthCheck.Enabled { | ||||
| 		entry, err = c.AddFunc("Repository health check", setting.Cron.RepoHealthCheck.Schedule, models.GitFsck) | ||||
| 		if err != nil { | ||||
| 			log.Fatal(4, "Cron[Repository health check]: %v", err) | ||||
| 			log.Fatal("Cron[Repository health check]: %v", err) | ||||
| 		} | ||||
| 		if setting.Cron.RepoHealthCheck.RunAtStart { | ||||
| 			entry.Prev = time.Now() | ||||
|  | @ -47,7 +47,7 @@ func NewContext() { | |||
| 	if setting.Cron.CheckRepoStats.Enabled { | ||||
| 		entry, err = c.AddFunc("Check repository statistics", setting.Cron.CheckRepoStats.Schedule, models.CheckRepoStats) | ||||
| 		if err != nil { | ||||
| 			log.Fatal(4, "Cron[Check repository statistics]: %v", err) | ||||
| 			log.Fatal("Cron[Check repository statistics]: %v", err) | ||||
| 		} | ||||
| 		if setting.Cron.CheckRepoStats.RunAtStart { | ||||
| 			entry.Prev = time.Now() | ||||
|  | @ -58,7 +58,7 @@ func NewContext() { | |||
| 	if setting.Cron.ArchiveCleanup.Enabled { | ||||
| 		entry, err = c.AddFunc("Clean up old repository archives", setting.Cron.ArchiveCleanup.Schedule, models.DeleteOldRepositoryArchives) | ||||
| 		if err != nil { | ||||
| 			log.Fatal(4, "Cron[Clean up old repository archives]: %v", err) | ||||
| 			log.Fatal("Cron[Clean up old repository archives]: %v", err) | ||||
| 		} | ||||
| 		if setting.Cron.ArchiveCleanup.RunAtStart { | ||||
| 			entry.Prev = time.Now() | ||||
|  | @ -69,7 +69,7 @@ func NewContext() { | |||
| 	if setting.Cron.SyncExternalUsers.Enabled { | ||||
| 		entry, err = c.AddFunc("Synchronize external users", setting.Cron.SyncExternalUsers.Schedule, models.SyncExternalUsers) | ||||
| 		if err != nil { | ||||
| 			log.Fatal(4, "Cron[Synchronize external users]: %v", err) | ||||
| 			log.Fatal("Cron[Synchronize external users]: %v", err) | ||||
| 		} | ||||
| 		if setting.Cron.SyncExternalUsers.RunAtStart { | ||||
| 			entry.Prev = time.Now() | ||||
|  | @ -80,7 +80,7 @@ func NewContext() { | |||
| 	if setting.Cron.DeletedBranchesCleanup.Enabled { | ||||
| 		entry, err = c.AddFunc("Remove old deleted branches", setting.Cron.DeletedBranchesCleanup.Schedule, models.RemoveOldDeletedBranches) | ||||
| 		if err != nil { | ||||
| 			log.Fatal(4, "Cron[Remove old deleted branches]: %v", err) | ||||
| 			log.Fatal("Cron[Remove old deleted branches]: %v", err) | ||||
| 		} | ||||
| 		if setting.Cron.DeletedBranchesCleanup.RunAtStart { | ||||
| 			entry.Prev = time.Now() | ||||
|  |  | |||
|  | @ -116,7 +116,7 @@ func populateIssueIndexer() { | |||
| 			Collaborate: util.OptionalBoolFalse, | ||||
| 		}) | ||||
| 		if err != nil { | ||||
| 			log.Error(4, "SearchRepositoryByName: %v", err) | ||||
| 			log.Error("SearchRepositoryByName: %v", err) | ||||
| 			continue | ||||
| 		} | ||||
| 		if len(repos) == 0 { | ||||
|  | @ -130,11 +130,11 @@ func populateIssueIndexer() { | |||
| 				IsPull:   util.OptionalBoolNone, | ||||
| 			}) | ||||
| 			if err != nil { | ||||
| 				log.Error(4, "Issues: %v", err) | ||||
| 				log.Error("Issues: %v", err) | ||||
| 				continue | ||||
| 			} | ||||
| 			if err = models.IssueList(is).LoadDiscussComments(); err != nil { | ||||
| 				log.Error(4, "LoadComments: %v", err) | ||||
| 				log.Error("LoadComments: %v", err) | ||||
| 				continue | ||||
| 			} | ||||
| 			for _, issue := range is { | ||||
|  | @ -166,7 +166,7 @@ func DeleteRepoIssueIndexer(repo *models.Repository) { | |||
| 	var ids []int64 | ||||
| 	ids, err := models.GetIssueIDsByRepoID(repo.ID) | ||||
| 	if err != nil { | ||||
| 		log.Error(4, "getIssueIDsByRepoID failed: %v", err) | ||||
| 		log.Error("getIssueIDsByRepoID failed: %v", err) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
|  |  | |||
|  | @ -53,7 +53,7 @@ func (l *LevelQueue) Run() error { | |||
| 		bs, err := l.queue.RPop() | ||||
| 		if err != nil { | ||||
| 			if err != levelqueue.ErrNotFound { | ||||
| 				log.Error(4, "RPop: %v", err) | ||||
| 				log.Error("RPop: %v", err) | ||||
| 			} | ||||
| 			time.Sleep(time.Millisecond * 100) | ||||
| 			continue | ||||
|  | @ -67,7 +67,7 @@ func (l *LevelQueue) Run() error { | |||
| 		var data IndexerData | ||||
| 		err = json.Unmarshal(bs, &data) | ||||
| 		if err != nil { | ||||
| 			log.Error(4, "Unmarshal: %v", err) | ||||
| 			log.Error("Unmarshal: %v", err) | ||||
| 			time.Sleep(time.Millisecond * 100) | ||||
| 			continue | ||||
| 		} | ||||
|  | @ -77,11 +77,11 @@ func (l *LevelQueue) Run() error { | |||
| 		if data.IsDelete { | ||||
| 			if data.ID > 0 { | ||||
| 				if err = l.indexer.Delete(data.ID); err != nil { | ||||
| 					log.Error(4, "indexer.Delete: %v", err) | ||||
| 					log.Error("indexer.Delete: %v", err) | ||||
| 				} | ||||
| 			} else if len(data.IDs) > 0 { | ||||
| 				if err = l.indexer.Delete(data.IDs...); err != nil { | ||||
| 					log.Error(4, "indexer.Delete: %v", err) | ||||
| 					log.Error("indexer.Delete: %v", err) | ||||
| 				} | ||||
| 			} | ||||
| 			time.Sleep(time.Millisecond * 10) | ||||
|  |  | |||
|  | @ -68,7 +68,7 @@ func (update RepoIndexerUpdate) AddToFlushingBatch(batch rupture.FlushingBatch) | |||
| 	case RepoIndexerOpDelete: | ||||
| 		return batch.Delete(id) | ||||
| 	default: | ||||
| 		log.Error(4, "Unrecognized repo indexer op: %d", update.Op) | ||||
| 		log.Error("Unrecognized repo indexer op: %d", update.Op) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | @ -78,17 +78,17 @@ func InitRepoIndexer(populateIndexer func() error) { | |||
| 	var err error | ||||
| 	repoIndexer, err = openIndexer(setting.Indexer.RepoPath, repoIndexerLatestVersion) | ||||
| 	if err != nil { | ||||
| 		log.Fatal(4, "InitRepoIndexer: %v", err) | ||||
| 		log.Fatal("InitRepoIndexer: %v", err) | ||||
| 	} | ||||
| 	if repoIndexer != nil { | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	if err = createRepoIndexer(setting.Indexer.RepoPath, repoIndexerLatestVersion); err != nil { | ||||
| 		log.Fatal(4, "CreateRepoIndexer: %v", err) | ||||
| 		log.Fatal("CreateRepoIndexer: %v", err) | ||||
| 	} | ||||
| 	if err = populateIndexer(); err != nil { | ||||
| 		log.Fatal(4, "PopulateRepoIndex: %v", err) | ||||
| 		log.Fatal("PopulateRepoIndex: %v", err) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | @ -135,7 +135,7 @@ func filenameIndexerID(repoID int64, filename string) string { | |||
| func filenameOfIndexerID(indexerID string) string { | ||||
| 	index := strings.IndexByte(indexerID, '_') | ||||
| 	if index == -1 { | ||||
| 		log.Error(4, "Unexpected ID in repo indexer: %s", indexerID) | ||||
| 		log.Error("Unexpected ID in repo indexer: %s", indexerID) | ||||
| 	} | ||||
| 	return indexerID[index+1:] | ||||
| } | ||||
|  |  | |||
|  | @ -333,7 +333,7 @@ func PutHandler(ctx *context.Context) { | |||
| 		ctx.Resp.WriteHeader(500) | ||||
| 		fmt.Fprintf(ctx.Resp, `{"message":"%s"}`, err) | ||||
| 		if err = repository.RemoveLFSMetaObjectByOid(rv.Oid); err != nil { | ||||
| 			log.Error(4, "RemoveLFSMetaObjectByOid: %v", err) | ||||
| 			log.Error("RemoveLFSMetaObjectByOid: %v", err) | ||||
| 		} | ||||
| 		return | ||||
| 	} | ||||
|  |  | |||
							
								
								
									
										328
									
								
								modules/log/base.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										328
									
								
								modules/log/base.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,328 @@ | |||
| // 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 "" | ||||
| } | ||||
							
								
								
									
										277
									
								
								modules/log/base_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										277
									
								
								modules/log/base_test.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,277 @@ | |||
| // 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 ( | ||||
| 	"fmt" | ||||
| 	"strings" | ||||
| 	"testing" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| ) | ||||
| 
 | ||||
| type CallbackWriteCloser struct { | ||||
| 	callback func([]byte, bool) | ||||
| } | ||||
| 
 | ||||
| func (c CallbackWriteCloser) Write(p []byte) (int, error) { | ||||
| 	c.callback(p, false) | ||||
| 	return len(p), nil | ||||
| } | ||||
| 
 | ||||
| func (c CallbackWriteCloser) Close() error { | ||||
| 	c.callback(nil, true) | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func TestBaseLogger(t *testing.T) { | ||||
| 	var written []byte | ||||
| 	var closed bool | ||||
| 
 | ||||
| 	c := CallbackWriteCloser{ | ||||
| 		callback: func(p []byte, close bool) { | ||||
| 			written = p | ||||
| 			closed = close | ||||
| 		}, | ||||
| 	} | ||||
| 	prefix := "TestPrefix " | ||||
| 	b := BaseLogger{ | ||||
| 		out:    c, | ||||
| 		Level:  INFO, | ||||
| 		Flags:  LstdFlags | LUTC, | ||||
| 		Prefix: prefix, | ||||
| 	} | ||||
| 	location, _ := time.LoadLocation("EST") | ||||
| 
 | ||||
| 	date := time.Date(2019, time.January, 13, 22, 3, 30, 15, location) | ||||
| 
 | ||||
| 	dateString := date.UTC().Format("2006/01/02 15:04:05") | ||||
| 
 | ||||
| 	event := Event{ | ||||
| 		level:    INFO, | ||||
| 		msg:      "TEST MSG", | ||||
| 		caller:   "CALLER", | ||||
| 		filename: "FULL/FILENAME", | ||||
| 		line:     1, | ||||
| 		time:     date, | ||||
| 	} | ||||
| 
 | ||||
| 	assert.Equal(t, INFO, b.GetLevel()) | ||||
| 
 | ||||
| 	expected := fmt.Sprintf("%s%s %s:%d:%s [%c] %s\n", prefix, dateString, event.filename, event.line, event.caller, strings.ToUpper(event.level.String())[0], event.msg) | ||||
| 	b.LogEvent(&event) | ||||
| 	assert.Equal(t, expected, string(written)) | ||||
| 	assert.Equal(t, false, closed) | ||||
| 	written = written[:0] | ||||
| 
 | ||||
| 	event.level = DEBUG | ||||
| 	expected = "" | ||||
| 	b.LogEvent(&event) | ||||
| 	assert.Equal(t, expected, string(written)) | ||||
| 	assert.Equal(t, false, closed) | ||||
| 
 | ||||
| 	event.level = TRACE | ||||
| 	expected = "" | ||||
| 	b.LogEvent(&event) | ||||
| 	assert.Equal(t, expected, string(written)) | ||||
| 	assert.Equal(t, false, closed) | ||||
| 
 | ||||
| 	event.level = WARN | ||||
| 	expected = fmt.Sprintf("%s%s %s:%d:%s [%c] %s\n", prefix, dateString, event.filename, event.line, event.caller, strings.ToUpper(event.level.String())[0], event.msg) | ||||
| 	b.LogEvent(&event) | ||||
| 	assert.Equal(t, expected, string(written)) | ||||
| 	assert.Equal(t, false, closed) | ||||
| 	written = written[:0] | ||||
| 
 | ||||
| 	event.level = ERROR | ||||
| 	expected = fmt.Sprintf("%s%s %s:%d:%s [%c] %s\n", prefix, dateString, event.filename, event.line, event.caller, strings.ToUpper(event.level.String())[0], event.msg) | ||||
| 	b.LogEvent(&event) | ||||
| 	assert.Equal(t, expected, string(written)) | ||||
| 	assert.Equal(t, false, closed) | ||||
| 	written = written[:0] | ||||
| 
 | ||||
| 	event.level = CRITICAL | ||||
| 	expected = fmt.Sprintf("%s%s %s:%d:%s [%c] %s\n", prefix, dateString, event.filename, event.line, event.caller, strings.ToUpper(event.level.String())[0], event.msg) | ||||
| 	b.LogEvent(&event) | ||||
| 	assert.Equal(t, expected, string(written)) | ||||
| 	assert.Equal(t, false, closed) | ||||
| 	written = written[:0] | ||||
| 
 | ||||
| 	b.Close() | ||||
| 	assert.Equal(t, true, closed) | ||||
| } | ||||
| 
 | ||||
| func TestBaseLoggerDated(t *testing.T) { | ||||
| 	var written []byte | ||||
| 	var closed bool | ||||
| 
 | ||||
| 	c := CallbackWriteCloser{ | ||||
| 		callback: func(p []byte, close bool) { | ||||
| 			written = p | ||||
| 			closed = close | ||||
| 		}, | ||||
| 	} | ||||
| 	prefix := "" | ||||
| 	b := BaseLogger{ | ||||
| 		out:    c, | ||||
| 		Level:  WARN, | ||||
| 		Flags:  Ldate | Ltime | Lmicroseconds | Lshortfile | Llevel, | ||||
| 		Prefix: prefix, | ||||
| 	} | ||||
| 
 | ||||
| 	location, _ := time.LoadLocation("EST") | ||||
| 
 | ||||
| 	date := time.Date(2019, time.January, 13, 22, 3, 30, 115, location) | ||||
| 
 | ||||
| 	dateString := date.Format("2006/01/02 15:04:05.000000") | ||||
| 
 | ||||
| 	event := Event{ | ||||
| 		level:    WARN, | ||||
| 		msg:      "TEST MESSAGE TEST\n", | ||||
| 		caller:   "CALLER", | ||||
| 		filename: "FULL/FILENAME", | ||||
| 		line:     1, | ||||
| 		time:     date, | ||||
| 	} | ||||
| 
 | ||||
| 	assert.Equal(t, WARN, b.GetLevel()) | ||||
| 
 | ||||
| 	expected := fmt.Sprintf("%s%s %s:%d [%s] %s", prefix, dateString, "FILENAME", event.line, strings.ToUpper(event.level.String()), event.msg) | ||||
| 	b.LogEvent(&event) | ||||
| 	assert.Equal(t, expected, string(written)) | ||||
| 	assert.Equal(t, false, closed) | ||||
| 	written = written[:0] | ||||
| 
 | ||||
| 	event.level = INFO | ||||
| 	expected = "" | ||||
| 	b.LogEvent(&event) | ||||
| 	assert.Equal(t, expected, string(written)) | ||||
| 	assert.Equal(t, false, closed) | ||||
| 	written = written[:0] | ||||
| 
 | ||||
| 	event.level = ERROR | ||||
| 	expected = fmt.Sprintf("%s%s %s:%d [%s] %s", prefix, dateString, "FILENAME", event.line, strings.ToUpper(event.level.String()), event.msg) | ||||
| 	b.LogEvent(&event) | ||||
| 	assert.Equal(t, expected, string(written)) | ||||
| 	assert.Equal(t, false, closed) | ||||
| 	written = written[:0] | ||||
| 
 | ||||
| 	event.level = DEBUG | ||||
| 	expected = "" | ||||
| 	b.LogEvent(&event) | ||||
| 	assert.Equal(t, expected, string(written)) | ||||
| 	assert.Equal(t, false, closed) | ||||
| 	written = written[:0] | ||||
| 
 | ||||
| 	event.level = CRITICAL | ||||
| 	expected = fmt.Sprintf("%s%s %s:%d [%s] %s", prefix, dateString, "FILENAME", event.line, strings.ToUpper(event.level.String()), event.msg) | ||||
| 	b.LogEvent(&event) | ||||
| 	assert.Equal(t, expected, string(written)) | ||||
| 	assert.Equal(t, false, closed) | ||||
| 	written = written[:0] | ||||
| 
 | ||||
| 	event.level = TRACE | ||||
| 	expected = "" | ||||
| 	b.LogEvent(&event) | ||||
| 	assert.Equal(t, expected, string(written)) | ||||
| 	assert.Equal(t, false, closed) | ||||
| 	written = written[:0] | ||||
| 
 | ||||
| 	b.Close() | ||||
| 	assert.Equal(t, true, closed) | ||||
| } | ||||
| 
 | ||||
| func TestBaseLoggerMultiLineNoFlagsRegexp(t *testing.T) { | ||||
| 	var written []byte | ||||
| 	var closed bool | ||||
| 
 | ||||
| 	c := CallbackWriteCloser{ | ||||
| 		callback: func(p []byte, close bool) { | ||||
| 			written = p | ||||
| 			closed = close | ||||
| 		}, | ||||
| 	} | ||||
| 	prefix := "" | ||||
| 	b := BaseLogger{ | ||||
| 		Level:           DEBUG, | ||||
| 		StacktraceLevel: ERROR, | ||||
| 		Flags:           -1, | ||||
| 		Prefix:          prefix, | ||||
| 		Expression:      "FILENAME", | ||||
| 	} | ||||
| 	b.createLogger(c) | ||||
| 
 | ||||
| 	location, _ := time.LoadLocation("EST") | ||||
| 
 | ||||
| 	date := time.Date(2019, time.January, 13, 22, 3, 30, 115, location) | ||||
| 
 | ||||
| 	event := Event{ | ||||
| 		level:    DEBUG, | ||||
| 		msg:      "TEST\nMESSAGE\nTEST", | ||||
| 		caller:   "CALLER", | ||||
| 		filename: "FULL/FILENAME", | ||||
| 		line:     1, | ||||
| 		time:     date, | ||||
| 	} | ||||
| 
 | ||||
| 	assert.Equal(t, DEBUG, b.GetLevel()) | ||||
| 
 | ||||
| 	expected := "TEST\n\tMESSAGE\n\tTEST\n" | ||||
| 	b.LogEvent(&event) | ||||
| 	assert.Equal(t, expected, string(written)) | ||||
| 	assert.Equal(t, false, closed) | ||||
| 	written = written[:0] | ||||
| 
 | ||||
| 	event.filename = "ELSEWHERE" | ||||
| 
 | ||||
| 	b.LogEvent(&event) | ||||
| 	assert.Equal(t, "", string(written)) | ||||
| 	assert.Equal(t, false, closed) | ||||
| 	written = written[:0] | ||||
| 
 | ||||
| 	event.caller = "FILENAME" | ||||
| 	b.LogEvent(&event) | ||||
| 	assert.Equal(t, expected, string(written)) | ||||
| 	assert.Equal(t, false, closed) | ||||
| 	written = written[:0] | ||||
| 
 | ||||
| 	event = Event{ | ||||
| 		level:    DEBUG, | ||||
| 		msg:      "TEST\nFILENAME\nTEST", | ||||
| 		caller:   "CALLER", | ||||
| 		filename: "FULL/ELSEWHERE", | ||||
| 		line:     1, | ||||
| 		time:     date, | ||||
| 	} | ||||
| 	expected = "TEST\n\tFILENAME\n\tTEST\n" | ||||
| 	b.LogEvent(&event) | ||||
| 	assert.Equal(t, expected, string(written)) | ||||
| 	assert.Equal(t, false, closed) | ||||
| 	written = written[:0] | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| func TestBrokenRegexp(t *testing.T) { | ||||
| 	var closed bool | ||||
| 
 | ||||
| 	c := CallbackWriteCloser{ | ||||
| 		callback: func(p []byte, close bool) { | ||||
| 			closed = close | ||||
| 		}, | ||||
| 	} | ||||
| 
 | ||||
| 	b := BaseLogger{ | ||||
| 		Level:           DEBUG, | ||||
| 		StacktraceLevel: ERROR, | ||||
| 		Flags:           -1, | ||||
| 		Prefix:          prefix, | ||||
| 		Expression:      "\\", | ||||
| 	} | ||||
| 	b.createLogger(c) | ||||
| 	assert.Empty(t, b.regexp) | ||||
| 	b.Close() | ||||
| 	assert.Equal(t, true, closed) | ||||
| } | ||||
							
								
								
									
										348
									
								
								modules/log/colors.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										348
									
								
								modules/log/colors.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,348 @@ | |||
| // 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 ( | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| ) | ||||
| 
 | ||||
| const escape = "\033" | ||||
| 
 | ||||
| // ColorAttribute defines a single SGR Code
 | ||||
| type ColorAttribute int | ||||
| 
 | ||||
| // Base ColorAttributes
 | ||||
| const ( | ||||
| 	Reset ColorAttribute = iota | ||||
| 	Bold | ||||
| 	Faint | ||||
| 	Italic | ||||
| 	Underline | ||||
| 	BlinkSlow | ||||
| 	BlinkRapid | ||||
| 	ReverseVideo | ||||
| 	Concealed | ||||
| 	CrossedOut | ||||
| ) | ||||
| 
 | ||||
| // Foreground text colors
 | ||||
| const ( | ||||
| 	FgBlack ColorAttribute = iota + 30 | ||||
| 	FgRed | ||||
| 	FgGreen | ||||
| 	FgYellow | ||||
| 	FgBlue | ||||
| 	FgMagenta | ||||
| 	FgCyan | ||||
| 	FgWhite | ||||
| ) | ||||
| 
 | ||||
| // Foreground Hi-Intensity text colors
 | ||||
| const ( | ||||
| 	FgHiBlack ColorAttribute = iota + 90 | ||||
| 	FgHiRed | ||||
| 	FgHiGreen | ||||
| 	FgHiYellow | ||||
| 	FgHiBlue | ||||
| 	FgHiMagenta | ||||
| 	FgHiCyan | ||||
| 	FgHiWhite | ||||
| ) | ||||
| 
 | ||||
| // Background text colors
 | ||||
| const ( | ||||
| 	BgBlack ColorAttribute = iota + 40 | ||||
| 	BgRed | ||||
| 	BgGreen | ||||
| 	BgYellow | ||||
| 	BgBlue | ||||
| 	BgMagenta | ||||
| 	BgCyan | ||||
| 	BgWhite | ||||
| ) | ||||
| 
 | ||||
| // Background Hi-Intensity text colors
 | ||||
| const ( | ||||
| 	BgHiBlack ColorAttribute = iota + 100 | ||||
| 	BgHiRed | ||||
| 	BgHiGreen | ||||
| 	BgHiYellow | ||||
| 	BgHiBlue | ||||
| 	BgHiMagenta | ||||
| 	BgHiCyan | ||||
| 	BgHiWhite | ||||
| ) | ||||
| 
 | ||||
| var colorAttributeToString = map[ColorAttribute]string{ | ||||
| 	Reset:        "Reset", | ||||
| 	Bold:         "Bold", | ||||
| 	Faint:        "Faint", | ||||
| 	Italic:       "Italic", | ||||
| 	Underline:    "Underline", | ||||
| 	BlinkSlow:    "BlinkSlow", | ||||
| 	BlinkRapid:   "BlinkRapid", | ||||
| 	ReverseVideo: "ReverseVideo", | ||||
| 	Concealed:    "Concealed", | ||||
| 	CrossedOut:   "CrossedOut", | ||||
| 	FgBlack:      "FgBlack", | ||||
| 	FgRed:        "FgRed", | ||||
| 	FgGreen:      "FgGreen", | ||||
| 	FgYellow:     "FgYellow", | ||||
| 	FgBlue:       "FgBlue", | ||||
| 	FgMagenta:    "FgMagenta", | ||||
| 	FgCyan:       "FgCyan", | ||||
| 	FgWhite:      "FgWhite", | ||||
| 	FgHiBlack:    "FgHiBlack", | ||||
| 	FgHiRed:      "FgHiRed", | ||||
| 	FgHiGreen:    "FgHiGreen", | ||||
| 	FgHiYellow:   "FgHiYellow", | ||||
| 	FgHiBlue:     "FgHiBlue", | ||||
| 	FgHiMagenta:  "FgHiMagenta", | ||||
| 	FgHiCyan:     "FgHiCyan", | ||||
| 	FgHiWhite:    "FgHiWhite", | ||||
| 	BgBlack:      "BgBlack", | ||||
| 	BgRed:        "BgRed", | ||||
| 	BgGreen:      "BgGreen", | ||||
| 	BgYellow:     "BgYellow", | ||||
| 	BgBlue:       "BgBlue", | ||||
| 	BgMagenta:    "BgMagenta", | ||||
| 	BgCyan:       "BgCyan", | ||||
| 	BgWhite:      "BgWhite", | ||||
| 	BgHiBlack:    "BgHiBlack", | ||||
| 	BgHiRed:      "BgHiRed", | ||||
| 	BgHiGreen:    "BgHiGreen", | ||||
| 	BgHiYellow:   "BgHiYellow", | ||||
| 	BgHiBlue:     "BgHiBlue", | ||||
| 	BgHiMagenta:  "BgHiMagenta", | ||||
| 	BgHiCyan:     "BgHiCyan", | ||||
| 	BgHiWhite:    "BgHiWhite", | ||||
| } | ||||
| 
 | ||||
| func (c *ColorAttribute) String() string { | ||||
| 	return colorAttributeToString[*c] | ||||
| } | ||||
| 
 | ||||
| var colorAttributeFromString = map[string]ColorAttribute{} | ||||
| 
 | ||||
| // ColorAttributeFromString will return a ColorAttribute given a string
 | ||||
| func ColorAttributeFromString(from string) ColorAttribute { | ||||
| 	lowerFrom := strings.TrimSpace(strings.ToLower(from)) | ||||
| 	return colorAttributeFromString[lowerFrom] | ||||
| } | ||||
| 
 | ||||
| // ColorString converts a list of ColorAttributes to a color string
 | ||||
| func ColorString(attrs ...ColorAttribute) string { | ||||
| 	return string(ColorBytes(attrs...)) | ||||
| } | ||||
| 
 | ||||
| // ColorBytes converts a list of ColorAttributes to a byte array
 | ||||
| func ColorBytes(attrs ...ColorAttribute) []byte { | ||||
| 	bytes := make([]byte, 0, 20) | ||||
| 	bytes = append(bytes, escape[0], '[') | ||||
| 	if len(attrs) > 0 { | ||||
| 		bytes = append(bytes, strconv.Itoa(int(attrs[0]))...) | ||||
| 		for _, a := range attrs[1:] { | ||||
| 			bytes = append(bytes, ';') | ||||
| 			bytes = append(bytes, strconv.Itoa(int(a))...) | ||||
| 		} | ||||
| 	} else { | ||||
| 		bytes = append(bytes, strconv.Itoa(int(Bold))...) | ||||
| 	} | ||||
| 	bytes = append(bytes, 'm') | ||||
| 	return bytes | ||||
| } | ||||
| 
 | ||||
| var levelToColor = map[Level]string{ | ||||
| 	TRACE:    ColorString(Bold, FgCyan), | ||||
| 	DEBUG:    ColorString(Bold, FgBlue), | ||||
| 	INFO:     ColorString(Bold, FgGreen), | ||||
| 	WARN:     ColorString(Bold, FgYellow), | ||||
| 	ERROR:    ColorString(Bold, FgRed), | ||||
| 	CRITICAL: ColorString(Bold, BgMagenta), | ||||
| 	FATAL:    ColorString(Bold, BgRed), | ||||
| 	NONE:     ColorString(Reset), | ||||
| } | ||||
| 
 | ||||
| var resetBytes = ColorBytes(Reset) | ||||
| var fgCyanBytes = ColorBytes(FgCyan) | ||||
| var fgGreenBytes = ColorBytes(FgGreen) | ||||
| var fgBoldBytes = ColorBytes(Bold) | ||||
| 
 | ||||
| type protectedANSIWriterMode int | ||||
| 
 | ||||
| const ( | ||||
| 	escapeAll protectedANSIWriterMode = iota | ||||
| 	allowColor | ||||
| 	removeColor | ||||
| ) | ||||
| 
 | ||||
| type protectedANSIWriter struct { | ||||
| 	w    io.Writer | ||||
| 	mode protectedANSIWriterMode | ||||
| } | ||||
| 
 | ||||
| // Write will protect against unusual characters
 | ||||
| func (c *protectedANSIWriter) Write(bytes []byte) (int, error) { | ||||
| 	end := len(bytes) | ||||
| 	totalWritten := 0 | ||||
| normalLoop: | ||||
| 	for i := 0; i < end; { | ||||
| 		lasti := i | ||||
| 
 | ||||
| 		if c.mode == escapeAll { | ||||
| 			for i < end && (bytes[i] >= ' ' || bytes[i] == '\n') { | ||||
| 				i++ | ||||
| 			} | ||||
| 		} else { | ||||
| 			for i < end && bytes[i] >= ' ' { | ||||
| 				i++ | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		if i > lasti { | ||||
| 			written, err := c.w.Write(bytes[lasti:i]) | ||||
| 			totalWritten = totalWritten + written | ||||
| 			if err != nil { | ||||
| 				return totalWritten, err | ||||
| 			} | ||||
| 
 | ||||
| 		} | ||||
| 		if i >= end { | ||||
| 			break | ||||
| 		} | ||||
| 
 | ||||
| 		// If we're not just escaping all we should prefix all newlines with a \t
 | ||||
| 		if c.mode != escapeAll { | ||||
| 			if bytes[i] == '\n' { | ||||
| 				written, err := c.w.Write([]byte{'\n', '\t'}) | ||||
| 				if written > 0 { | ||||
| 					totalWritten++ | ||||
| 				} | ||||
| 				if err != nil { | ||||
| 					return totalWritten, err | ||||
| 				} | ||||
| 				i++ | ||||
| 				continue normalLoop | ||||
| 			} | ||||
| 
 | ||||
| 			if bytes[i] == escape[0] && i+1 < end && bytes[i+1] == '[' { | ||||
| 				for j := i + 2; j < end; j++ { | ||||
| 					if bytes[j] >= '0' && bytes[j] <= '9' { | ||||
| 						continue | ||||
| 					} | ||||
| 					if bytes[j] == ';' { | ||||
| 						continue | ||||
| 					} | ||||
| 					if bytes[j] == 'm' { | ||||
| 						if c.mode == allowColor { | ||||
| 							written, err := c.w.Write(bytes[i : j+1]) | ||||
| 							totalWritten = totalWritten + written | ||||
| 							if err != nil { | ||||
| 								return totalWritten, err | ||||
| 							} | ||||
| 						} else { | ||||
| 							totalWritten = j | ||||
| 						} | ||||
| 						i = j + 1 | ||||
| 						continue normalLoop | ||||
| 					} | ||||
| 					break | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		// Process naughty character
 | ||||
| 		if _, err := fmt.Fprintf(c.w, `\%#o03d`, bytes[i]); err != nil { | ||||
| 			return totalWritten, err | ||||
| 		} | ||||
| 		i++ | ||||
| 		totalWritten++ | ||||
| 	} | ||||
| 	return totalWritten, nil | ||||
| } | ||||
| 
 | ||||
| // ColoredValue will Color the provided value
 | ||||
| type ColoredValue struct { | ||||
| 	ColorBytes *[]byte | ||||
| 	ResetBytes *[]byte | ||||
| 	Value      *interface{} | ||||
| } | ||||
| 
 | ||||
| // NewColoredValue is a helper function to create a ColoredValue from a Value
 | ||||
| // If no color is provided it defaults to Bold with standard Reset
 | ||||
| // If a ColoredValue is provided it is not changed
 | ||||
| func NewColoredValue(value interface{}, color ...ColorAttribute) *ColoredValue { | ||||
| 	return NewColoredValuePointer(&value, color...) | ||||
| } | ||||
| 
 | ||||
| // NewColoredValuePointer is a helper function to create a ColoredValue from a Value Pointer
 | ||||
| // If no color is provided it defaults to Bold with standard Reset
 | ||||
| // If a ColoredValue is provided it is not changed
 | ||||
| func NewColoredValuePointer(value *interface{}, color ...ColorAttribute) *ColoredValue { | ||||
| 	if val, ok := (*value).(*ColoredValue); ok { | ||||
| 		return val | ||||
| 	} | ||||
| 	if len(color) > 0 { | ||||
| 		bytes := ColorBytes(color...) | ||||
| 		return &ColoredValue{ | ||||
| 			ColorBytes: &bytes, | ||||
| 			ResetBytes: &resetBytes, | ||||
| 			Value:      value, | ||||
| 		} | ||||
| 	} | ||||
| 	return &ColoredValue{ | ||||
| 		ColorBytes: &fgBoldBytes, | ||||
| 		ResetBytes: &resetBytes, | ||||
| 		Value:      value, | ||||
| 	} | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| // NewColoredValueBytes creates a value from the provided value with color bytes
 | ||||
| // If a ColoredValue is provided it is not changed
 | ||||
| func NewColoredValueBytes(value interface{}, colorBytes *[]byte) *ColoredValue { | ||||
| 	if val, ok := value.(*ColoredValue); ok { | ||||
| 		return val | ||||
| 	} | ||||
| 	return &ColoredValue{ | ||||
| 		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)) | ||||
| 	fmt.Fprintf(&protectedANSIWriter{w: s}, fmtString(s, c), *(cv.Value)) | ||||
| 	s.Write([]byte(*cv.ResetBytes)) | ||||
| } | ||||
| 
 | ||||
| func fmtString(s fmt.State, c rune) string { | ||||
| 	var width, precision string | ||||
| 	base := make([]byte, 0, 8) | ||||
| 	base = append(base, '%') | ||||
| 	for _, c := range []byte(" +-#0") { | ||||
| 		if s.Flag(int(c)) { | ||||
| 			base = append(base, c) | ||||
| 		} | ||||
| 	} | ||||
| 	if w, ok := s.Width(); ok { | ||||
| 		width = strconv.Itoa(w) | ||||
| 	} | ||||
| 	if p, ok := s.Precision(); ok { | ||||
| 		precision = "." + strconv.Itoa(p) | ||||
| 	} | ||||
| 	return fmt.Sprintf("%s%s%s%c", base, width, precision, c) | ||||
| } | ||||
| 
 | ||||
| func init() { | ||||
| 	for attr, from := range colorAttributeToString { | ||||
| 		colorAttributeFromString[strings.ToLower(from)] = attr | ||||
| 	} | ||||
| } | ||||
|  | @ -1,4 +1,5 @@ | |||
| // Copyright 2014 The Gogs Authors. All rights reserved.
 | ||||
| // 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.
 | ||||
| 
 | ||||
|  | @ -7,73 +8,60 @@ package log | |||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"io" | ||||
| 	"log" | ||||
| 	"net" | ||||
| ) | ||||
| 
 | ||||
| // ConnWriter implements LoggerInterface.
 | ||||
| // it writes messages in keep-live tcp connection.
 | ||||
| type ConnWriter struct { | ||||
| 	lg             *log.Logger | ||||
| type connWriter struct { | ||||
| 	innerWriter    io.WriteCloser | ||||
| 	ReconnectOnMsg bool   `json:"reconnectOnMsg"` | ||||
| 	Reconnect      bool   `json:"reconnect"` | ||||
| 	Net            string `json:"net"` | ||||
| 	Addr           string `json:"addr"` | ||||
| 	Level          int    `json:"level"` | ||||
| } | ||||
| 
 | ||||
| // NewConn creates new ConnWrite returning as LoggerInterface.
 | ||||
| func NewConn() LoggerInterface { | ||||
| 	conn := new(ConnWriter) | ||||
| 	conn.Level = TRACE | ||||
| 	return conn | ||||
| } | ||||
| 
 | ||||
| // Init inits connection writer with json config.
 | ||||
| // json config only need key "level".
 | ||||
| func (cw *ConnWriter) Init(jsonconfig string) error { | ||||
| 	return json.Unmarshal([]byte(jsonconfig), cw) | ||||
| } | ||||
| 
 | ||||
| // WriteMsg writes message in connection.
 | ||||
| // if connection is down, try to re-connect.
 | ||||
| func (cw *ConnWriter) WriteMsg(msg string, skip, level int) error { | ||||
| 	if cw.Level > level { | ||||
| 		return nil | ||||
| // Close the inner writer
 | ||||
| func (i *connWriter) Close() error { | ||||
| 	if i.innerWriter != nil { | ||||
| 		return i.innerWriter.Close() | ||||
| 	} | ||||
| 	if cw.neededConnectOnMsg() { | ||||
| 		if err := cw.connect(); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if cw.ReconnectOnMsg { | ||||
| 		defer cw.innerWriter.Close() | ||||
| 	} | ||||
| 	cw.lg.Println(msg) | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // Flush no things for this implementation
 | ||||
| func (cw *ConnWriter) Flush() { | ||||
| } | ||||
| 
 | ||||
| // Destroy destroy connection writer and close tcp listener.
 | ||||
| func (cw *ConnWriter) Destroy() { | ||||
| 	if cw.innerWriter == nil { | ||||
| 		return | ||||
| 	} | ||||
| 	cw.innerWriter.Close() | ||||
| } | ||||
| 
 | ||||
| func (cw *ConnWriter) connect() error { | ||||
| 	if cw.innerWriter != nil { | ||||
| 		cw.innerWriter.Close() | ||||
| 		cw.innerWriter = nil | ||||
| // Write the data to the connection
 | ||||
| func (i *connWriter) Write(p []byte) (int, error) { | ||||
| 	if i.neededConnectOnMsg() { | ||||
| 		if err := i.connect(); err != nil { | ||||
| 			return 0, err | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	conn, err := net.Dial(cw.Net, cw.Addr) | ||||
| 	if i.ReconnectOnMsg { | ||||
| 		defer i.innerWriter.Close() | ||||
| 	} | ||||
| 
 | ||||
| 	return i.innerWriter.Write(p) | ||||
| } | ||||
| 
 | ||||
| func (i *connWriter) neededConnectOnMsg() bool { | ||||
| 	if i.Reconnect { | ||||
| 		i.Reconnect = false | ||||
| 		return true | ||||
| 	} | ||||
| 
 | ||||
| 	if i.innerWriter == nil { | ||||
| 		return true | ||||
| 	} | ||||
| 
 | ||||
| 	return i.ReconnectOnMsg | ||||
| } | ||||
| 
 | ||||
| func (i *connWriter) connect() error { | ||||
| 	if i.innerWriter != nil { | ||||
| 		i.innerWriter.Close() | ||||
| 		i.innerWriter = nil | ||||
| 	} | ||||
| 
 | ||||
| 	conn, err := net.Dial(i.Net, i.Addr) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | @ -82,22 +70,50 @@ func (cw *ConnWriter) connect() error { | |||
| 		tcpConn.SetKeepAlive(true) | ||||
| 	} | ||||
| 
 | ||||
| 	cw.innerWriter = conn | ||||
| 	cw.lg = log.New(conn, "", log.Ldate|log.Ltime) | ||||
| 	i.innerWriter = conn | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (cw *ConnWriter) neededConnectOnMsg() bool { | ||||
| 	if cw.Reconnect { | ||||
| 		cw.Reconnect = false | ||||
| 		return true | ||||
| 	} | ||||
| // ConnLogger implements LoggerProvider.
 | ||||
| // it writes messages in keep-live tcp connection.
 | ||||
| type ConnLogger struct { | ||||
| 	BaseLogger | ||||
| 	ReconnectOnMsg bool   `json:"reconnectOnMsg"` | ||||
| 	Reconnect      bool   `json:"reconnect"` | ||||
| 	Net            string `json:"net"` | ||||
| 	Addr           string `json:"addr"` | ||||
| } | ||||
| 
 | ||||
| 	if cw.innerWriter == nil { | ||||
| 		return true | ||||
| 	} | ||||
| // NewConn creates new ConnLogger returning as LoggerProvider.
 | ||||
| func NewConn() LoggerProvider { | ||||
| 	conn := new(ConnLogger) | ||||
| 	conn.Level = TRACE | ||||
| 	return conn | ||||
| } | ||||
| 
 | ||||
| 	return cw.ReconnectOnMsg | ||||
| // Init inits connection writer with json config.
 | ||||
| // json config only need key "level".
 | ||||
| func (log *ConnLogger) Init(jsonconfig string) error { | ||||
| 	err := json.Unmarshal([]byte(jsonconfig), log) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	log.createLogger(&connWriter{ | ||||
| 		ReconnectOnMsg: log.ReconnectOnMsg, | ||||
| 		Reconnect:      log.Reconnect, | ||||
| 		Net:            log.Net, | ||||
| 		Addr:           log.Addr, | ||||
| 	}, log.Level) | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // Flush does nothing for this implementation
 | ||||
| func (log *ConnLogger) Flush() { | ||||
| } | ||||
| 
 | ||||
| // GetName returns the default name for this implementation
 | ||||
| func (log *ConnLogger) GetName() string { | ||||
| 	return "conn" | ||||
| } | ||||
| 
 | ||||
| func init() { | ||||
|  |  | |||
							
								
								
									
										240
									
								
								modules/log/conn_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										240
									
								
								modules/log/conn_test.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,240 @@ | |||
| // 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 ( | ||||
| 	"fmt" | ||||
| 	"io/ioutil" | ||||
| 	"net" | ||||
| 	"strings" | ||||
| 	"sync" | ||||
| 	"testing" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| ) | ||||
| 
 | ||||
| func listenReadAndClose(t *testing.T, l net.Listener, expected string) { | ||||
| 	conn, err := l.Accept() | ||||
| 	assert.NoError(t, err) | ||||
| 	defer conn.Close() | ||||
| 	written, err := ioutil.ReadAll(conn) | ||||
| 
 | ||||
| 	assert.NoError(t, err) | ||||
| 	assert.Equal(t, expected, string(written)) | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| func TestConnLogger(t *testing.T) { | ||||
| 	var written []byte | ||||
| 
 | ||||
| 	protocol := "tcp" | ||||
| 	address := ":3099" | ||||
| 
 | ||||
| 	l, err := net.Listen(protocol, address) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 	defer l.Close() | ||||
| 
 | ||||
| 	prefix := "TestPrefix " | ||||
| 	level := INFO | ||||
| 	flags := LstdFlags | LUTC | Lfuncname | ||||
| 
 | ||||
| 	logger := NewConn() | ||||
| 	connLogger := logger.(*ConnLogger) | ||||
| 
 | ||||
| 	logger.Init(fmt.Sprintf("{\"prefix\":\"%s\",\"level\":\"%s\",\"flags\":%d,\"reconnectOnMsg\":%t,\"reconnect\":%t,\"net\":\"%s\",\"addr\":\"%s\"}", prefix, level.String(), flags, true, true, protocol, address)) | ||||
| 
 | ||||
| 	assert.Equal(t, flags, connLogger.Flags) | ||||
| 	assert.Equal(t, level, connLogger.Level) | ||||
| 	assert.Equal(t, level, logger.GetLevel()) | ||||
| 
 | ||||
| 	location, _ := time.LoadLocation("EST") | ||||
| 
 | ||||
| 	date := time.Date(2019, time.January, 13, 22, 3, 30, 15, location) | ||||
| 
 | ||||
| 	dateString := date.UTC().Format("2006/01/02 15:04:05") | ||||
| 
 | ||||
| 	event := Event{ | ||||
| 		level:    INFO, | ||||
| 		msg:      "TEST MSG", | ||||
| 		caller:   "CALLER", | ||||
| 		filename: "FULL/FILENAME", | ||||
| 		line:     1, | ||||
| 		time:     date, | ||||
| 	} | ||||
| 	expected := fmt.Sprintf("%s%s %s:%d:%s [%c] %s\n", prefix, dateString, event.filename, event.line, event.caller, strings.ToUpper(event.level.String())[0], event.msg) | ||||
| 	var wg sync.WaitGroup | ||||
| 	wg.Add(2) | ||||
| 	go func() { | ||||
| 		defer wg.Done() | ||||
| 		listenReadAndClose(t, l, expected) | ||||
| 	}() | ||||
| 	go func() { | ||||
| 		defer wg.Done() | ||||
| 		err := logger.LogEvent(&event) | ||||
| 		assert.NoError(t, err) | ||||
| 	}() | ||||
| 	wg.Wait() | ||||
| 
 | ||||
| 	written = written[:0] | ||||
| 
 | ||||
| 	event.level = WARN | ||||
| 	expected = fmt.Sprintf("%s%s %s:%d:%s [%c] %s\n", prefix, dateString, event.filename, event.line, event.caller, strings.ToUpper(event.level.String())[0], event.msg) | ||||
| 	wg.Add(2) | ||||
| 	go func() { | ||||
| 		defer wg.Done() | ||||
| 		listenReadAndClose(t, l, expected) | ||||
| 	}() | ||||
| 	go func() { | ||||
| 		defer wg.Done() | ||||
| 		err := logger.LogEvent(&event) | ||||
| 		assert.NoError(t, err) | ||||
| 	}() | ||||
| 	wg.Wait() | ||||
| 
 | ||||
| 	logger.Close() | ||||
| } | ||||
| 
 | ||||
| func TestConnLoggerBadConfig(t *testing.T) { | ||||
| 	logger := NewConn() | ||||
| 
 | ||||
| 	err := logger.Init("{") | ||||
| 	assert.Equal(t, "unexpected end of JSON input", err.Error()) | ||||
| 	logger.Close() | ||||
| } | ||||
| 
 | ||||
| func TestConnLoggerCloseBeforeSend(t *testing.T) { | ||||
| 	protocol := "tcp" | ||||
| 	address := ":3099" | ||||
| 
 | ||||
| 	prefix := "TestPrefix " | ||||
| 	level := INFO | ||||
| 	flags := LstdFlags | LUTC | Lfuncname | ||||
| 
 | ||||
| 	logger := NewConn() | ||||
| 
 | ||||
| 	logger.Init(fmt.Sprintf("{\"prefix\":\"%s\",\"level\":\"%s\",\"flags\":%d,\"reconnectOnMsg\":%t,\"reconnect\":%t,\"net\":\"%s\",\"addr\":\"%s\"}", prefix, level.String(), flags, false, false, protocol, address)) | ||||
| 	logger.Close() | ||||
| } | ||||
| 
 | ||||
| func TestConnLoggerFailConnect(t *testing.T) { | ||||
| 	protocol := "tcp" | ||||
| 	address := ":3099" | ||||
| 
 | ||||
| 	prefix := "TestPrefix " | ||||
| 	level := INFO | ||||
| 	flags := LstdFlags | LUTC | Lfuncname | ||||
| 
 | ||||
| 	logger := NewConn() | ||||
| 
 | ||||
| 	logger.Init(fmt.Sprintf("{\"prefix\":\"%s\",\"level\":\"%s\",\"flags\":%d,\"reconnectOnMsg\":%t,\"reconnect\":%t,\"net\":\"%s\",\"addr\":\"%s\"}", prefix, level.String(), flags, false, false, protocol, address)) | ||||
| 
 | ||||
| 	assert.Equal(t, level, logger.GetLevel()) | ||||
| 
 | ||||
| 	location, _ := time.LoadLocation("EST") | ||||
| 
 | ||||
| 	date := time.Date(2019, time.January, 13, 22, 3, 30, 15, location) | ||||
| 
 | ||||
| 	//dateString := date.UTC().Format("2006/01/02 15:04:05")
 | ||||
| 
 | ||||
| 	event := Event{ | ||||
| 		level:    INFO, | ||||
| 		msg:      "TEST MSG", | ||||
| 		caller:   "CALLER", | ||||
| 		filename: "FULL/FILENAME", | ||||
| 		line:     1, | ||||
| 		time:     date, | ||||
| 	} | ||||
| 
 | ||||
| 	err := logger.LogEvent(&event) | ||||
| 	assert.Error(t, err) | ||||
| 
 | ||||
| 	logger.Close() | ||||
| } | ||||
| 
 | ||||
| func TestConnLoggerClose(t *testing.T) { | ||||
| 	var written []byte | ||||
| 
 | ||||
| 	protocol := "tcp" | ||||
| 	address := ":3099" | ||||
| 
 | ||||
| 	l, err := net.Listen(protocol, address) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 	defer l.Close() | ||||
| 
 | ||||
| 	prefix := "TestPrefix " | ||||
| 	level := INFO | ||||
| 	flags := LstdFlags | LUTC | Lfuncname | ||||
| 
 | ||||
| 	logger := NewConn() | ||||
| 	connLogger := logger.(*ConnLogger) | ||||
| 
 | ||||
| 	logger.Init(fmt.Sprintf("{\"prefix\":\"%s\",\"level\":\"%s\",\"flags\":%d,\"reconnectOnMsg\":%t,\"reconnect\":%t,\"net\":\"%s\",\"addr\":\"%s\"}", prefix, level.String(), flags, false, false, protocol, address)) | ||||
| 
 | ||||
| 	assert.Equal(t, flags, connLogger.Flags) | ||||
| 	assert.Equal(t, level, connLogger.Level) | ||||
| 	assert.Equal(t, level, logger.GetLevel()) | ||||
| 	location, _ := time.LoadLocation("EST") | ||||
| 
 | ||||
| 	date := time.Date(2019, time.January, 13, 22, 3, 30, 15, location) | ||||
| 
 | ||||
| 	dateString := date.UTC().Format("2006/01/02 15:04:05") | ||||
| 
 | ||||
| 	event := Event{ | ||||
| 		level:    INFO, | ||||
| 		msg:      "TEST MSG", | ||||
| 		caller:   "CALLER", | ||||
| 		filename: "FULL/FILENAME", | ||||
| 		line:     1, | ||||
| 		time:     date, | ||||
| 	} | ||||
| 	expected := fmt.Sprintf("%s%s %s:%d:%s [%c] %s\n", prefix, dateString, event.filename, event.line, event.caller, strings.ToUpper(event.level.String())[0], event.msg) | ||||
| 	var wg sync.WaitGroup | ||||
| 	wg.Add(2) | ||||
| 	go func() { | ||||
| 		defer wg.Done() | ||||
| 		err := logger.LogEvent(&event) | ||||
| 		assert.NoError(t, err) | ||||
| 		logger.Close() | ||||
| 	}() | ||||
| 	go func() { | ||||
| 		defer wg.Done() | ||||
| 		listenReadAndClose(t, l, expected) | ||||
| 	}() | ||||
| 	wg.Wait() | ||||
| 
 | ||||
| 	logger = NewConn() | ||||
| 	connLogger = logger.(*ConnLogger) | ||||
| 
 | ||||
| 	logger.Init(fmt.Sprintf("{\"prefix\":\"%s\",\"level\":\"%s\",\"flags\":%d,\"reconnectOnMsg\":%t,\"reconnect\":%t,\"net\":\"%s\",\"addr\":\"%s\"}", prefix, level.String(), flags, false, true, protocol, address)) | ||||
| 
 | ||||
| 	assert.Equal(t, flags, connLogger.Flags) | ||||
| 	assert.Equal(t, level, connLogger.Level) | ||||
| 	assert.Equal(t, level, logger.GetLevel()) | ||||
| 
 | ||||
| 	written = written[:0] | ||||
| 
 | ||||
| 	event.level = WARN | ||||
| 	expected = fmt.Sprintf("%s%s %s:%d:%s [%c] %s\n", prefix, dateString, event.filename, event.line, event.caller, strings.ToUpper(event.level.String())[0], event.msg) | ||||
| 	wg.Add(2) | ||||
| 	go func() { | ||||
| 		defer wg.Done() | ||||
| 		listenReadAndClose(t, l, expected) | ||||
| 	}() | ||||
| 	go func() { | ||||
| 		defer wg.Done() | ||||
| 		err := logger.LogEvent(&event) | ||||
| 		assert.NoError(t, err) | ||||
| 		logger.Close() | ||||
| 
 | ||||
| 	}() | ||||
| 	wg.Wait() | ||||
| 	logger.Flush() | ||||
| 	logger.Close() | ||||
| } | ||||
|  | @ -1,4 +1,5 @@ | |||
| // Copyright 2014 The Gogs Authors. All rights reserved.
 | ||||
| // 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.
 | ||||
| 
 | ||||
|  | @ -6,75 +7,72 @@ package log | |||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"log" | ||||
| 	"io" | ||||
| 	"os" | ||||
| 	"runtime" | ||||
| ) | ||||
| 
 | ||||
| // Brush brush type
 | ||||
| type Brush func(string) string | ||||
| // CanColorStdout reports if we can color the Stdout
 | ||||
| // Although we could do terminal sniffing and the like - in reality
 | ||||
| // most tools on *nix are happy to display ansi colors.
 | ||||
| // We will terminal sniff on Windows in console_windows.go
 | ||||
| var CanColorStdout = true | ||||
| 
 | ||||
| // NewBrush create a brush according color
 | ||||
| func NewBrush(color string) Brush { | ||||
| 	pre := "\033[" | ||||
| 	reset := "\033[0m" | ||||
| 	return func(text string) string { | ||||
| 		return pre + color + "m" + text + reset | ||||
| 	} | ||||
| // CanColorStderr reports if we can color the Stderr
 | ||||
| var CanColorStderr = true | ||||
| 
 | ||||
| type nopWriteCloser struct { | ||||
| 	w io.WriteCloser | ||||
| } | ||||
| 
 | ||||
| var colors = []Brush{ | ||||
| 	NewBrush("1;36"), // Trace      cyan
 | ||||
| 	NewBrush("1;34"), // Debug      blue
 | ||||
| 	NewBrush("1;32"), // Info       green
 | ||||
| 	NewBrush("1;33"), // Warn       yellow
 | ||||
| 	NewBrush("1;31"), // Error      red
 | ||||
| 	NewBrush("1;35"), // Critical   purple
 | ||||
| 	NewBrush("1;31"), // Fatal      red
 | ||||
| func (n *nopWriteCloser) Write(p []byte) (int, error) { | ||||
| 	return n.w.Write(p) | ||||
| } | ||||
| 
 | ||||
| // ConsoleWriter implements LoggerInterface and writes messages to terminal.
 | ||||
| type ConsoleWriter struct { | ||||
| 	lg    *log.Logger | ||||
| 	Level int `json:"level"` | ||||
| func (n *nopWriteCloser) Close() error { | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // NewConsole create ConsoleWriter returning as LoggerInterface.
 | ||||
| func NewConsole() LoggerInterface { | ||||
| 	return &ConsoleWriter{ | ||||
| 		lg:    log.New(os.Stdout, "", log.Ldate|log.Ltime), | ||||
| 		Level: TRACE, | ||||
| 	} | ||||
| // ConsoleLogger implements LoggerProvider and writes messages to terminal.
 | ||||
| type ConsoleLogger struct { | ||||
| 	BaseLogger | ||||
| 	Stderr bool `json:"stderr"` | ||||
| } | ||||
| 
 | ||||
| // NewConsoleLogger create ConsoleLogger returning as LoggerProvider.
 | ||||
| func NewConsoleLogger() LoggerProvider { | ||||
| 	log := &ConsoleLogger{} | ||||
| 	log.createLogger(&nopWriteCloser{ | ||||
| 		w: os.Stdout, | ||||
| 	}) | ||||
| 	return log | ||||
| } | ||||
| 
 | ||||
| // Init inits connection writer with json config.
 | ||||
| // json config only need key "level".
 | ||||
| func (cw *ConsoleWriter) Init(config string) error { | ||||
| 	return json.Unmarshal([]byte(config), cw) | ||||
| } | ||||
| 
 | ||||
| // WriteMsg writes message in console.
 | ||||
| // if OS is windows, ignore colors.
 | ||||
| func (cw *ConsoleWriter) WriteMsg(msg string, skip, level int) error { | ||||
| 	if cw.Level > level { | ||||
| 		return nil | ||||
| func (log *ConsoleLogger) Init(config string) error { | ||||
| 	err := json.Unmarshal([]byte(config), log) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if runtime.GOOS == "windows" { | ||||
| 		cw.lg.Println(msg) | ||||
| 	if log.Stderr { | ||||
| 		log.createLogger(&nopWriteCloser{ | ||||
| 			w: os.Stderr, | ||||
| 		}) | ||||
| 	} else { | ||||
| 		cw.lg.Println(colors[level](msg)) | ||||
| 		log.createLogger(log.out) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // Flush when log should be flushed
 | ||||
| func (cw *ConsoleWriter) Flush() { | ||||
| func (log *ConsoleLogger) Flush() { | ||||
| } | ||||
| 
 | ||||
| // Destroy when writer is destroy
 | ||||
| func (cw *ConsoleWriter) Destroy() { | ||||
| // GetName returns the default name for this implementation
 | ||||
| func (log *ConsoleLogger) GetName() string { | ||||
| 	return "console" | ||||
| } | ||||
| 
 | ||||
| func init() { | ||||
| 	Register("console", NewConsole) | ||||
| 	Register("console", NewConsoleLogger) | ||||
| } | ||||
|  |  | |||
							
								
								
									
										137
									
								
								modules/log/console_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								modules/log/console_test.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,137 @@ | |||
| // 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 ( | ||||
| 	"fmt" | ||||
| 	"strings" | ||||
| 	"testing" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| ) | ||||
| 
 | ||||
| func TestConsoleLoggerBadConfig(t *testing.T) { | ||||
| 	logger := NewConsoleLogger() | ||||
| 
 | ||||
| 	err := logger.Init("{") | ||||
| 	assert.Equal(t, "unexpected end of JSON input", err.Error()) | ||||
| 	logger.Close() | ||||
| } | ||||
| 
 | ||||
| func TestConsoleLoggerMinimalConfig(t *testing.T) { | ||||
| 	for _, level := range Levels() { | ||||
| 		var written []byte | ||||
| 		var closed bool | ||||
| 
 | ||||
| 		c := CallbackWriteCloser{ | ||||
| 			callback: func(p []byte, close bool) { | ||||
| 				written = p | ||||
| 				closed = close | ||||
| 			}, | ||||
| 		} | ||||
| 		prefix := "" | ||||
| 		flags := LstdFlags | ||||
| 
 | ||||
| 		cw := NewConsoleLogger() | ||||
| 		realCW := cw.(*ConsoleLogger) | ||||
| 		cw.Init(fmt.Sprintf("{\"level\":\"%s\"}", level)) | ||||
| 		nwc := realCW.out.(*nopWriteCloser) | ||||
| 		nwc.w = c | ||||
| 
 | ||||
| 		assert.Equal(t, flags, realCW.Flags) | ||||
| 		assert.Equal(t, FromString(level), realCW.Level) | ||||
| 		assert.Equal(t, FromString(level), cw.GetLevel()) | ||||
| 		assert.Equal(t, prefix, realCW.Prefix) | ||||
| 		assert.Equal(t, "", string(written)) | ||||
| 		cw.Close() | ||||
| 		assert.Equal(t, false, closed) | ||||
| 
 | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func TestConsoleLogger(t *testing.T) { | ||||
| 	var written []byte | ||||
| 	var closed bool | ||||
| 
 | ||||
| 	c := CallbackWriteCloser{ | ||||
| 		callback: func(p []byte, close bool) { | ||||
| 			written = p | ||||
| 			closed = close | ||||
| 		}, | ||||
| 	} | ||||
| 	prefix := "TestPrefix " | ||||
| 	level := INFO | ||||
| 	flags := LstdFlags | LUTC | Lfuncname | ||||
| 
 | ||||
| 	cw := NewConsoleLogger() | ||||
| 	realCW := cw.(*ConsoleLogger) | ||||
| 	realCW.Colorize = false | ||||
| 	nwc := realCW.out.(*nopWriteCloser) | ||||
| 	nwc.w = c | ||||
| 
 | ||||
| 	cw.Init(fmt.Sprintf("{\"expression\":\"FILENAME\",\"prefix\":\"%s\",\"level\":\"%s\",\"flags\":%d}", prefix, level.String(), flags)) | ||||
| 
 | ||||
| 	assert.Equal(t, flags, realCW.Flags) | ||||
| 	assert.Equal(t, level, realCW.Level) | ||||
| 	assert.Equal(t, level, cw.GetLevel()) | ||||
| 
 | ||||
| 	location, _ := time.LoadLocation("EST") | ||||
| 
 | ||||
| 	date := time.Date(2019, time.January, 13, 22, 3, 30, 15, location) | ||||
| 
 | ||||
| 	dateString := date.UTC().Format("2006/01/02 15:04:05") | ||||
| 
 | ||||
| 	event := Event{ | ||||
| 		level:    INFO, | ||||
| 		msg:      "TEST MSG", | ||||
| 		caller:   "CALLER", | ||||
| 		filename: "FULL/FILENAME", | ||||
| 		line:     1, | ||||
| 		time:     date, | ||||
| 	} | ||||
| 
 | ||||
| 	expected := fmt.Sprintf("%s%s %s:%d:%s [%c] %s\n", prefix, dateString, event.filename, event.line, event.caller, strings.ToUpper(event.level.String())[0], event.msg) | ||||
| 	cw.LogEvent(&event) | ||||
| 	assert.Equal(t, expected, string(written)) | ||||
| 	assert.Equal(t, false, closed) | ||||
| 	written = written[:0] | ||||
| 
 | ||||
| 	event.level = DEBUG | ||||
| 	expected = "" | ||||
| 	cw.LogEvent(&event) | ||||
| 	assert.Equal(t, expected, string(written)) | ||||
| 	assert.Equal(t, false, closed) | ||||
| 
 | ||||
| 	event.level = TRACE | ||||
| 	expected = "" | ||||
| 	cw.LogEvent(&event) | ||||
| 	assert.Equal(t, expected, string(written)) | ||||
| 	assert.Equal(t, false, closed) | ||||
| 
 | ||||
| 	nonMatchEvent := Event{ | ||||
| 		level:    INFO, | ||||
| 		msg:      "TEST MSG", | ||||
| 		caller:   "CALLER", | ||||
| 		filename: "FULL/FI_LENAME", | ||||
| 		line:     1, | ||||
| 		time:     date, | ||||
| 	} | ||||
| 	event.level = INFO | ||||
| 	expected = "" | ||||
| 	cw.LogEvent(&nonMatchEvent) | ||||
| 	assert.Equal(t, expected, string(written)) | ||||
| 	assert.Equal(t, false, closed) | ||||
| 
 | ||||
| 	event.level = WARN | ||||
| 	expected = fmt.Sprintf("%s%s %s:%d:%s [%c] %s\n", prefix, dateString, event.filename, event.line, event.caller, strings.ToUpper(event.level.String())[0], event.msg) | ||||
| 	cw.LogEvent(&event) | ||||
| 	assert.Equal(t, expected, string(written)) | ||||
| 	assert.Equal(t, false, closed) | ||||
| 	written = written[:0] | ||||
| 
 | ||||
| 	cw.Close() | ||||
| 	assert.Equal(t, false, closed) | ||||
| } | ||||
							
								
								
									
										43
									
								
								modules/log/console_windows.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								modules/log/console_windows.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,43 @@ | |||
| // 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 ( | ||||
| 	"os" | ||||
| 
 | ||||
| 	"github.com/mattn/go-isatty" | ||||
| 	"golang.org/x/sys/windows" | ||||
| ) | ||||
| 
 | ||||
| func enableVTMode(console windows.Handle) bool { | ||||
| 	mode := uint32(0) | ||||
| 	err := windows.GetConsoleMode(console, &mode) | ||||
| 	if err != nil { | ||||
| 		return false | ||||
| 	} | ||||
| 
 | ||||
| 	// EnableVirtualTerminalProcessing is the console mode to allow ANSI code
 | ||||
| 	// interpretation on the console. See:
 | ||||
| 	// https://docs.microsoft.com/en-us/windows/console/setconsolemode
 | ||||
| 	// It only works on windows 10. Earlier terminals will fail with an err which we will
 | ||||
| 	// handle to say don't color
 | ||||
| 	mode = mode | windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING | ||||
| 	err = windows.SetConsoleMode(console, mode) | ||||
| 	return err == nil | ||||
| } | ||||
| 
 | ||||
| func init() { | ||||
| 	if isatty.IsTerminal(os.Stdout.Fd()) { | ||||
| 		CanColorStdout = enableVTMode(windows.Stdout) | ||||
| 	} else { | ||||
| 		CanColorStdout = isatty.IsCygwinTerminal(os.Stderr.Fd()) | ||||
| 	} | ||||
| 
 | ||||
| 	if isatty.IsTerminal(os.Stderr.Fd()) { | ||||
| 		CanColorStderr = enableVTMode(windows.Stderr) | ||||
| 	} else { | ||||
| 		CanColorStderr = isatty.IsCygwinTerminal(os.Stderr.Fd()) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										62
									
								
								modules/log/errors.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								modules/log/errors.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,62 @@ | |||
| // 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 "fmt" | ||||
| 
 | ||||
| // ErrTimeout represents a "Timeout" kind of error.
 | ||||
| type ErrTimeout struct { | ||||
| 	Name     string | ||||
| 	Provider string | ||||
| } | ||||
| 
 | ||||
| // IsErrTimeout checks if an error is a ErrTimeout.
 | ||||
| func IsErrTimeout(err error) bool { | ||||
| 	if err == nil { | ||||
| 		return false | ||||
| 	} | ||||
| 	_, ok := err.(ErrTimeout) | ||||
| 	return ok | ||||
| } | ||||
| 
 | ||||
| func (err ErrTimeout) Error() string { | ||||
| 	return fmt.Sprintf("Log Timeout for %s (%s)", err.Name, err.Provider) | ||||
| } | ||||
| 
 | ||||
| // ErrUnknownProvider represents a "Unknown Provider" kind of error.
 | ||||
| type ErrUnknownProvider struct { | ||||
| 	Provider string | ||||
| } | ||||
| 
 | ||||
| // IsErrUnknownProvider checks if an error is a ErrUnknownProvider.
 | ||||
| func IsErrUnknownProvider(err error) bool { | ||||
| 	if err == nil { | ||||
| 		return false | ||||
| 	} | ||||
| 	_, ok := err.(ErrUnknownProvider) | ||||
| 	return ok | ||||
| } | ||||
| 
 | ||||
| func (err ErrUnknownProvider) Error() string { | ||||
| 	return fmt.Sprintf("Unknown Log Provider \"%s\" (Was it registered?)", err.Provider) | ||||
| } | ||||
| 
 | ||||
| // ErrDuplicateName represents a Duplicate Name error
 | ||||
| type ErrDuplicateName struct { | ||||
| 	Name string | ||||
| } | ||||
| 
 | ||||
| // IsErrDuplicateName checks if an error is a ErrDuplicateName.
 | ||||
| func IsErrDuplicateName(err error) bool { | ||||
| 	if err == nil { | ||||
| 		return false | ||||
| 	} | ||||
| 	_, ok := err.(ErrDuplicateName) | ||||
| 	return ok | ||||
| } | ||||
| 
 | ||||
| func (err ErrDuplicateName) Error() string { | ||||
| 	return fmt.Sprintf("Duplicate named logger: %s", err.Name) | ||||
| } | ||||
							
								
								
									
										335
									
								
								modules/log/event.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										335
									
								
								modules/log/event.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,335 @@ | |||
| // 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 ( | ||||
| 	"fmt" | ||||
| 	"sync" | ||||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
| // Event represents a logging event
 | ||||
| type Event struct { | ||||
| 	level      Level | ||||
| 	msg        string | ||||
| 	caller     string | ||||
| 	filename   string | ||||
| 	line       int | ||||
| 	time       time.Time | ||||
| 	stacktrace string | ||||
| } | ||||
| 
 | ||||
| // EventLogger represents the behaviours of a logger
 | ||||
| type EventLogger interface { | ||||
| 	LogEvent(event *Event) error | ||||
| 	Close() | ||||
| 	Flush() | ||||
| 	GetLevel() Level | ||||
| 	GetStacktraceLevel() Level | ||||
| 	GetName() string | ||||
| } | ||||
| 
 | ||||
| // ChannelledLog represents a cached channel to a LoggerProvider
 | ||||
| type ChannelledLog struct { | ||||
| 	name           string | ||||
| 	provider       string | ||||
| 	queue          chan *Event | ||||
| 	loggerProvider LoggerProvider | ||||
| 	flush          chan bool | ||||
| 	close          chan bool | ||||
| 	closed         chan bool | ||||
| } | ||||
| 
 | ||||
| // NewChannelledLog a new logger instance with given logger provider and config.
 | ||||
| func NewChannelledLog(name, provider, config string, bufferLength int64) (*ChannelledLog, error) { | ||||
| 	if log, ok := providers[provider]; ok { | ||||
| 		l := &ChannelledLog{ | ||||
| 			queue:  make(chan *Event, bufferLength), | ||||
| 			flush:  make(chan bool), | ||||
| 			close:  make(chan bool), | ||||
| 			closed: make(chan bool), | ||||
| 		} | ||||
| 		l.loggerProvider = log() | ||||
| 		if err := l.loggerProvider.Init(config); err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		l.name = name | ||||
| 		l.provider = provider | ||||
| 		go l.Start() | ||||
| 		return l, nil | ||||
| 	} | ||||
| 	return nil, ErrUnknownProvider{provider} | ||||
| } | ||||
| 
 | ||||
| // Start processing the ChannelledLog
 | ||||
| func (l *ChannelledLog) Start() { | ||||
| 	for { | ||||
| 		select { | ||||
| 		case event, ok := <-l.queue: | ||||
| 			if !ok { | ||||
| 				l.closeLogger() | ||||
| 				return | ||||
| 			} | ||||
| 			l.loggerProvider.LogEvent(event) | ||||
| 		case _, ok := <-l.flush: | ||||
| 			if !ok { | ||||
| 				l.closeLogger() | ||||
| 				return | ||||
| 			} | ||||
| 			l.loggerProvider.Flush() | ||||
| 		case _, _ = <-l.close: | ||||
| 			l.closeLogger() | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // LogEvent logs an event to this ChannelledLog
 | ||||
| func (l *ChannelledLog) LogEvent(event *Event) error { | ||||
| 	select { | ||||
| 	case l.queue <- event: | ||||
| 		return nil | ||||
| 	case <-time.After(60 * time.Second): | ||||
| 		// We're blocked!
 | ||||
| 		return ErrTimeout{ | ||||
| 			Name:     l.name, | ||||
| 			Provider: l.provider, | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (l *ChannelledLog) closeLogger() { | ||||
| 	l.loggerProvider.Flush() | ||||
| 	l.loggerProvider.Close() | ||||
| 	l.closed <- true | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // Close this ChannelledLog
 | ||||
| func (l *ChannelledLog) Close() { | ||||
| 	l.close <- true | ||||
| 	<-l.closed | ||||
| } | ||||
| 
 | ||||
| // Flush this ChannelledLog
 | ||||
| func (l *ChannelledLog) Flush() { | ||||
| 	l.flush <- true | ||||
| } | ||||
| 
 | ||||
| // GetLevel gets the level of this ChannelledLog
 | ||||
| func (l *ChannelledLog) GetLevel() Level { | ||||
| 	return l.loggerProvider.GetLevel() | ||||
| } | ||||
| 
 | ||||
| // GetStacktraceLevel gets the level of this ChannelledLog
 | ||||
| func (l *ChannelledLog) GetStacktraceLevel() Level { | ||||
| 	return l.loggerProvider.GetStacktraceLevel() | ||||
| } | ||||
| 
 | ||||
| // GetName returns the name of this ChannelledLog
 | ||||
| func (l *ChannelledLog) GetName() string { | ||||
| 	return l.name | ||||
| } | ||||
| 
 | ||||
| // MultiChannelledLog represents a cached channel to a LoggerProvider
 | ||||
| type MultiChannelledLog struct { | ||||
| 	name            string | ||||
| 	bufferLength    int64 | ||||
| 	queue           chan *Event | ||||
| 	mutex           sync.Mutex | ||||
| 	loggers         map[string]EventLogger | ||||
| 	flush           chan bool | ||||
| 	close           chan bool | ||||
| 	started         bool | ||||
| 	level           Level | ||||
| 	stacktraceLevel Level | ||||
| 	closed          chan bool | ||||
| } | ||||
| 
 | ||||
| // NewMultiChannelledLog a new logger instance with given logger provider and config.
 | ||||
| func NewMultiChannelledLog(name string, bufferLength int64) *MultiChannelledLog { | ||||
| 	m := &MultiChannelledLog{ | ||||
| 		name:            name, | ||||
| 		queue:           make(chan *Event, bufferLength), | ||||
| 		flush:           make(chan bool), | ||||
| 		bufferLength:    bufferLength, | ||||
| 		loggers:         make(map[string]EventLogger), | ||||
| 		level:           NONE, | ||||
| 		stacktraceLevel: NONE, | ||||
| 		close:           make(chan bool), | ||||
| 		closed:          make(chan bool), | ||||
| 	} | ||||
| 	return m | ||||
| } | ||||
| 
 | ||||
| // AddLogger adds a logger to this MultiChannelledLog
 | ||||
| func (m *MultiChannelledLog) AddLogger(logger EventLogger) error { | ||||
| 	m.mutex.Lock() | ||||
| 	name := logger.GetName() | ||||
| 	if _, has := m.loggers[name]; has { | ||||
| 		m.mutex.Unlock() | ||||
| 		return ErrDuplicateName{name} | ||||
| 	} | ||||
| 	m.loggers[name] = logger | ||||
| 	if logger.GetLevel() < m.level { | ||||
| 		m.level = logger.GetLevel() | ||||
| 	} | ||||
| 	if logger.GetStacktraceLevel() < m.stacktraceLevel { | ||||
| 		m.stacktraceLevel = logger.GetStacktraceLevel() | ||||
| 	} | ||||
| 	m.mutex.Unlock() | ||||
| 	go m.Start() | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // DelLogger removes a sub logger from this MultiChannelledLog
 | ||||
| // NB: If you delete the last sublogger this logger will simply drop
 | ||||
| // log events
 | ||||
| func (m *MultiChannelledLog) DelLogger(name string) bool { | ||||
| 	m.mutex.Lock() | ||||
| 	logger, has := m.loggers[name] | ||||
| 	if !has { | ||||
| 		m.mutex.Unlock() | ||||
| 		return false | ||||
| 	} | ||||
| 	delete(m.loggers, name) | ||||
| 	m.internalResetLevel() | ||||
| 	m.mutex.Unlock() | ||||
| 	logger.Flush() | ||||
| 	logger.Close() | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| // GetEventLogger returns a sub logger from this MultiChannelledLog
 | ||||
| func (m *MultiChannelledLog) GetEventLogger(name string) EventLogger { | ||||
| 	m.mutex.Lock() | ||||
| 	defer m.mutex.Unlock() | ||||
| 	return m.loggers[name] | ||||
| } | ||||
| 
 | ||||
| // GetEventLoggerNames returns a list of names
 | ||||
| func (m *MultiChannelledLog) GetEventLoggerNames() []string { | ||||
| 	m.mutex.Lock() | ||||
| 	defer m.mutex.Unlock() | ||||
| 	var keys []string | ||||
| 	for k := range m.loggers { | ||||
| 		keys = append(keys, k) | ||||
| 	} | ||||
| 	return keys | ||||
| } | ||||
| 
 | ||||
| func (m *MultiChannelledLog) closeLoggers() { | ||||
| 	m.mutex.Lock() | ||||
| 	for _, logger := range m.loggers { | ||||
| 		logger.Flush() | ||||
| 		logger.Close() | ||||
| 	} | ||||
| 	m.mutex.Unlock() | ||||
| 	m.closed <- true | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // Start processing the MultiChannelledLog
 | ||||
| func (m *MultiChannelledLog) Start() { | ||||
| 	m.mutex.Lock() | ||||
| 	if m.started { | ||||
| 		m.mutex.Unlock() | ||||
| 		return | ||||
| 	} | ||||
| 	m.started = true | ||||
| 	m.mutex.Unlock() | ||||
| 	for { | ||||
| 		select { | ||||
| 		case event, ok := <-m.queue: | ||||
| 			if !ok { | ||||
| 				m.closeLoggers() | ||||
| 				return | ||||
| 			} | ||||
| 			m.mutex.Lock() | ||||
| 			for _, logger := range m.loggers { | ||||
| 				err := logger.LogEvent(event) | ||||
| 				if err != nil { | ||||
| 					fmt.Println(err) | ||||
| 				} | ||||
| 			} | ||||
| 			m.mutex.Unlock() | ||||
| 		case _, ok := <-m.flush: | ||||
| 			if !ok { | ||||
| 				m.closeLoggers() | ||||
| 				return | ||||
| 			} | ||||
| 			m.mutex.Lock() | ||||
| 			for _, logger := range m.loggers { | ||||
| 				logger.Flush() | ||||
| 			} | ||||
| 			m.mutex.Unlock() | ||||
| 		case <-m.close: | ||||
| 			m.closeLoggers() | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // LogEvent logs an event to this MultiChannelledLog
 | ||||
| func (m *MultiChannelledLog) LogEvent(event *Event) error { | ||||
| 	select { | ||||
| 	case m.queue <- event: | ||||
| 		return nil | ||||
| 	case <-time.After(60 * time.Second): | ||||
| 		// We're blocked!
 | ||||
| 		return ErrTimeout{ | ||||
| 			Name:     m.name, | ||||
| 			Provider: "MultiChannelledLog", | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Close this MultiChannelledLog
 | ||||
| func (m *MultiChannelledLog) Close() { | ||||
| 	m.close <- true | ||||
| 	<-m.closed | ||||
| } | ||||
| 
 | ||||
| // Flush this ChannelledLog
 | ||||
| func (m *MultiChannelledLog) Flush() { | ||||
| 	m.flush <- true | ||||
| } | ||||
| 
 | ||||
| // GetLevel gets the level of this MultiChannelledLog
 | ||||
| func (m *MultiChannelledLog) GetLevel() Level { | ||||
| 	return m.level | ||||
| } | ||||
| 
 | ||||
| // GetStacktraceLevel gets the level of this MultiChannelledLog
 | ||||
| func (m *MultiChannelledLog) GetStacktraceLevel() Level { | ||||
| 	return m.stacktraceLevel | ||||
| } | ||||
| 
 | ||||
| func (m *MultiChannelledLog) internalResetLevel() Level { | ||||
| 	m.level = NONE | ||||
| 	for _, logger := range m.loggers { | ||||
| 		level := logger.GetLevel() | ||||
| 		if level < m.level { | ||||
| 			m.level = level | ||||
| 		} | ||||
| 		level = logger.GetStacktraceLevel() | ||||
| 		if level < m.stacktraceLevel { | ||||
| 			m.stacktraceLevel = level | ||||
| 		} | ||||
| 	} | ||||
| 	return m.level | ||||
| } | ||||
| 
 | ||||
| // ResetLevel will reset the level of this MultiChannelledLog
 | ||||
| func (m *MultiChannelledLog) ResetLevel() Level { | ||||
| 	m.mutex.Lock() | ||||
| 	defer m.mutex.Unlock() | ||||
| 	return m.internalResetLevel() | ||||
| } | ||||
| 
 | ||||
| // GetName gets the name of this MultiChannelledLog
 | ||||
| func (m *MultiChannelledLog) GetName() string { | ||||
| 	return m.name | ||||
| } | ||||
|  | @ -5,10 +5,11 @@ | |||
| package log | ||||
| 
 | ||||
| import ( | ||||
| 	"bufio" | ||||
| 	"compress/gzip" | ||||
| 	"encoding/json" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"log" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"strings" | ||||
|  | @ -16,10 +17,10 @@ import ( | |||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
| // FileLogWriter implements LoggerInterface.
 | ||||
| // FileLogger implements LoggerProvider.
 | ||||
| // It writes messages by lines limit, file size limit, or time frequency.
 | ||||
| type FileLogWriter struct { | ||||
| 	*log.Logger | ||||
| type FileLogger struct { | ||||
| 	BaseLogger | ||||
| 	mw *MuxWriter | ||||
| 	// The opened file
 | ||||
| 	Filename string `json:"filename"` | ||||
|  | @ -35,47 +36,57 @@ type FileLogWriter struct { | |||
| 
 | ||||
| 	Rotate bool `json:"rotate"` | ||||
| 
 | ||||
| 	startLock sync.Mutex // Only one log can write to the file
 | ||||
| 	Compress         bool `json:"compress"` | ||||
| 	CompressionLevel int  `json:"compressionLevel"` | ||||
| 
 | ||||
| 	Level int `json:"level"` | ||||
| 	startLock sync.Mutex // Only one log can write to the file
 | ||||
| } | ||||
| 
 | ||||
| // MuxWriter an *os.File writer with locker.
 | ||||
| type MuxWriter struct { | ||||
| 	sync.Mutex | ||||
| 	fd *os.File | ||||
| 	mu    sync.Mutex | ||||
| 	fd    *os.File | ||||
| 	owner *FileLogger | ||||
| } | ||||
| 
 | ||||
| // Write writes to os.File.
 | ||||
| func (l *MuxWriter) Write(b []byte) (int, error) { | ||||
| 	l.Lock() | ||||
| 	defer l.Unlock() | ||||
| 	return l.fd.Write(b) | ||||
| func (mw *MuxWriter) Write(b []byte) (int, error) { | ||||
| 	mw.mu.Lock() | ||||
| 	defer mw.mu.Unlock() | ||||
| 	mw.owner.docheck(len(b)) | ||||
| 	return mw.fd.Write(b) | ||||
| } | ||||
| 
 | ||||
| // Close the internal writer
 | ||||
| func (mw *MuxWriter) Close() error { | ||||
| 	return mw.fd.Close() | ||||
| } | ||||
| 
 | ||||
| // SetFd sets os.File in writer.
 | ||||
| func (l *MuxWriter) SetFd(fd *os.File) { | ||||
| 	if l.fd != nil { | ||||
| 		l.fd.Close() | ||||
| func (mw *MuxWriter) SetFd(fd *os.File) { | ||||
| 	if mw.fd != nil { | ||||
| 		mw.fd.Close() | ||||
| 	} | ||||
| 	l.fd = fd | ||||
| 	mw.fd = fd | ||||
| } | ||||
| 
 | ||||
| // NewFileWriter create a FileLogWriter returning as LoggerInterface.
 | ||||
| func NewFileWriter() LoggerInterface { | ||||
| 	w := &FileLogWriter{ | ||||
| 		Filename: "", | ||||
| 		Maxsize:  1 << 28, //256 MB
 | ||||
| 		Daily:    true, | ||||
| 		Maxdays:  7, | ||||
| 		Rotate:   true, | ||||
| 		Level:    TRACE, | ||||
| // NewFileLogger create a FileLogger returning as LoggerProvider.
 | ||||
| func NewFileLogger() LoggerProvider { | ||||
| 	log := &FileLogger{ | ||||
| 		Filename:         "", | ||||
| 		Maxsize:          1 << 28, //256 MB
 | ||||
| 		Daily:            true, | ||||
| 		Maxdays:          7, | ||||
| 		Rotate:           true, | ||||
| 		Compress:         true, | ||||
| 		CompressionLevel: gzip.DefaultCompression, | ||||
| 	} | ||||
| 	log.Level = TRACE | ||||
| 	// use MuxWriter instead direct use os.File for lock write when rotate
 | ||||
| 	w.mw = new(MuxWriter) | ||||
| 	// set MuxWriter as Logger's io.Writer
 | ||||
| 	w.Logger = log.New(w.mw, "", log.Ldate|log.Ltime) | ||||
| 	return w | ||||
| 	log.mw = new(MuxWriter) | ||||
| 	log.mw.owner = log | ||||
| 
 | ||||
| 	return log | ||||
| } | ||||
| 
 | ||||
| // Init file logger with json config.
 | ||||
|  | @ -87,109 +98,131 @@ func NewFileWriter() LoggerInterface { | |||
| //	"maxdays":15,
 | ||||
| //	"rotate":true
 | ||||
| //	}
 | ||||
| func (w *FileLogWriter) Init(config string) error { | ||||
| 	if err := json.Unmarshal([]byte(config), w); err != nil { | ||||
| func (log *FileLogger) Init(config string) error { | ||||
| 	if err := json.Unmarshal([]byte(config), log); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if len(w.Filename) == 0 { | ||||
| 	if len(log.Filename) == 0 { | ||||
| 		return errors.New("config must have filename") | ||||
| 	} | ||||
| 	return w.StartLogger() | ||||
| 	// set MuxWriter as Logger's io.Writer
 | ||||
| 	log.createLogger(log.mw) | ||||
| 	return log.StartLogger() | ||||
| } | ||||
| 
 | ||||
| // StartLogger start file logger. create log file and set to locker-inside file writer.
 | ||||
| func (w *FileLogWriter) StartLogger() error { | ||||
| 	fd, err := w.createLogFile() | ||||
| func (log *FileLogger) StartLogger() error { | ||||
| 	fd, err := log.createLogFile() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	w.mw.SetFd(fd) | ||||
| 	return w.initFd() | ||||
| 	log.mw.SetFd(fd) | ||||
| 	return log.initFd() | ||||
| } | ||||
| 
 | ||||
| func (w *FileLogWriter) docheck(size int) { | ||||
| 	w.startLock.Lock() | ||||
| 	defer w.startLock.Unlock() | ||||
| 	if w.Rotate && ((w.Maxsize > 0 && w.maxsizeCursize >= w.Maxsize) || | ||||
| 		(w.Daily && time.Now().Day() != w.dailyOpenDate)) { | ||||
| 		if err := w.DoRotate(); err != nil { | ||||
| 			fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.Filename, err) | ||||
| func (log *FileLogger) docheck(size int) { | ||||
| 	log.startLock.Lock() | ||||
| 	defer log.startLock.Unlock() | ||||
| 	if log.Rotate && ((log.Maxsize > 0 && log.maxsizeCursize >= log.Maxsize) || | ||||
| 		(log.Daily && time.Now().Day() != log.dailyOpenDate)) { | ||||
| 		if err := log.DoRotate(); err != nil { | ||||
| 			fmt.Fprintf(os.Stderr, "FileLogger(%q): %s\n", log.Filename, err) | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
| 	w.maxsizeCursize += size | ||||
| 	log.maxsizeCursize += size | ||||
| } | ||||
| 
 | ||||
| // WriteMsg writes logger message into file.
 | ||||
| func (w *FileLogWriter) WriteMsg(msg string, skip, level int) error { | ||||
| 	if level < w.Level { | ||||
| 		return nil | ||||
| 	} | ||||
| 	n := 24 + len(msg) // 24 stand for the length "2013/06/23 21:00:22 [T] "
 | ||||
| 	w.docheck(n) | ||||
| 	w.Logger.Println(msg) | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (w *FileLogWriter) createLogFile() (*os.File, error) { | ||||
| func (log *FileLogger) createLogFile() (*os.File, error) { | ||||
| 	// Open the log file
 | ||||
| 	return os.OpenFile(w.Filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0660) | ||||
| 	return os.OpenFile(log.Filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0660) | ||||
| } | ||||
| 
 | ||||
| func (w *FileLogWriter) initFd() error { | ||||
| 	fd := w.mw.fd | ||||
| func (log *FileLogger) initFd() error { | ||||
| 	fd := log.mw.fd | ||||
| 	finfo, err := fd.Stat() | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("get stat: %v", err) | ||||
| 	} | ||||
| 	w.maxsizeCursize = int(finfo.Size()) | ||||
| 	w.dailyOpenDate = time.Now().Day() | ||||
| 	log.maxsizeCursize = int(finfo.Size()) | ||||
| 	log.dailyOpenDate = time.Now().Day() | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // DoRotate means it need to write file in new file.
 | ||||
| // new file name like xx.log.2013-01-01.2
 | ||||
| func (w *FileLogWriter) DoRotate() error { | ||||
| 	_, err := os.Lstat(w.Filename) | ||||
| func (log *FileLogger) DoRotate() error { | ||||
| 	_, err := os.Lstat(log.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(".%s.%03d", time.Now().Format("2006-01-02"), num) | ||||
| 			fname = log.Filename + fmt.Sprintf(".%s.%03d", time.Now().Format("2006-01-02"), num) | ||||
| 			_, err = os.Lstat(fname) | ||||
| 			if log.Compress && err != nil { | ||||
| 				_, err = os.Lstat(fname + ".gz") | ||||
| 			} | ||||
| 		} | ||||
| 		// return error if the last file checked still existed
 | ||||
| 		if err == nil { | ||||
| 			return fmt.Errorf("rotate: cannot find free log number to rename %s", w.Filename) | ||||
| 			return fmt.Errorf("rotate: cannot find free log number to rename %s", log.Filename) | ||||
| 		} | ||||
| 
 | ||||
| 		// block Logger's io.Writer
 | ||||
| 		w.mw.Lock() | ||||
| 		defer w.mw.Unlock() | ||||
| 
 | ||||
| 		fd := w.mw.fd | ||||
| 		fd := log.mw.fd | ||||
| 		fd.Close() | ||||
| 
 | ||||
| 		// close fd before rename
 | ||||
| 		// Rename the file to its newfound home
 | ||||
| 		if err = os.Rename(w.Filename, fname); err != nil { | ||||
| 		if err = os.Rename(log.Filename, fname); err != nil { | ||||
| 			return fmt.Errorf("Rotate: %v", err) | ||||
| 		} | ||||
| 
 | ||||
| 		if log.Compress { | ||||
| 			go compressOldLogFile(fname, log.CompressionLevel) | ||||
| 		} | ||||
| 
 | ||||
| 		// re-start logger
 | ||||
| 		if err = w.StartLogger(); err != nil { | ||||
| 		if err = log.StartLogger(); err != nil { | ||||
| 			return fmt.Errorf("Rotate StartLogger: %v", err) | ||||
| 		} | ||||
| 
 | ||||
| 		go w.deleteOldLog() | ||||
| 		go log.deleteOldLog() | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (w *FileLogWriter) deleteOldLog() { | ||||
| 	dir := filepath.Dir(w.Filename) | ||||
| func compressOldLogFile(fname string, compressionLevel int) error { | ||||
| 	reader, err := os.Open(fname) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	defer reader.Close() | ||||
| 	buffer := bufio.NewReader(reader) | ||||
| 	fw, err := os.OpenFile(fname+".gz", os.O_WRONLY|os.O_CREATE, 0660) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	defer fw.Close() | ||||
| 	zw, err := gzip.NewWriterLevel(fw, compressionLevel) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	defer zw.Close() | ||||
| 	_, err = buffer.WriteTo(zw) | ||||
| 	if err != nil { | ||||
| 		zw.Close() | ||||
| 		fw.Close() | ||||
| 		os.Remove(fname + ".gz") | ||||
| 		return err | ||||
| 	} | ||||
| 	reader.Close() | ||||
| 	return os.Remove(fname) | ||||
| } | ||||
| 
 | ||||
| func (log *FileLogger) deleteOldLog() { | ||||
| 	dir := filepath.Dir(log.Filename) | ||||
| 	filepath.Walk(dir, func(path string, info os.FileInfo, err error) (returnErr error) { | ||||
| 		defer func() { | ||||
| 			if r := recover(); r != nil { | ||||
|  | @ -197,8 +230,8 @@ func (w *FileLogWriter) deleteOldLog() { | |||
| 			} | ||||
| 		}() | ||||
| 
 | ||||
| 		if !info.IsDir() && info.ModTime().Unix() < (time.Now().Unix()-60*60*24*w.Maxdays) { | ||||
| 			if strings.HasPrefix(filepath.Base(path), filepath.Base(w.Filename)) { | ||||
| 		if !info.IsDir() && info.ModTime().Unix() < (time.Now().Unix()-60*60*24*log.Maxdays) { | ||||
| 			if strings.HasPrefix(filepath.Base(path), filepath.Base(log.Filename)) { | ||||
| 
 | ||||
| 				if err := os.Remove(path); err != nil { | ||||
| 					returnErr = fmt.Errorf("Failed to remove %s: %v", path, err) | ||||
|  | @ -209,18 +242,18 @@ func (w *FileLogWriter) deleteOldLog() { | |||
| 	}) | ||||
| } | ||||
| 
 | ||||
| // Destroy destroy file logger, close file writer.
 | ||||
| func (w *FileLogWriter) Destroy() { | ||||
| 	w.mw.fd.Close() | ||||
| } | ||||
| 
 | ||||
| // Flush flush file logger.
 | ||||
| // there are no buffering messages in file logger in memory.
 | ||||
| // flush file means sync file from disk.
 | ||||
| func (w *FileLogWriter) Flush() { | ||||
| 	w.mw.fd.Sync() | ||||
| func (log *FileLogger) Flush() { | ||||
| 	log.mw.fd.Sync() | ||||
| } | ||||
| 
 | ||||
| // GetName returns the default name for this implementation
 | ||||
| func (log *FileLogger) GetName() string { | ||||
| 	return "file" | ||||
| } | ||||
| 
 | ||||
| func init() { | ||||
| 	Register("file", NewFileWriter) | ||||
| 	Register("file", NewFileLogger) | ||||
| } | ||||
|  |  | |||
							
								
								
									
										247
									
								
								modules/log/file_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										247
									
								
								modules/log/file_test.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,247 @@ | |||
| // 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 ( | ||||
| 	"compress/gzip" | ||||
| 	"fmt" | ||||
| 	"io/ioutil" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"strings" | ||||
| 	"testing" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| ) | ||||
| 
 | ||||
| func TestFileLoggerFails(t *testing.T) { | ||||
| 	tmpDir, err := ioutil.TempDir("", "TestFileLogger") | ||||
| 	assert.NoError(t, err) | ||||
| 	defer os.RemoveAll(tmpDir) | ||||
| 
 | ||||
| 	prefix := "TestPrefix " | ||||
| 	level := INFO | ||||
| 	flags := LstdFlags | LUTC | Lfuncname | ||||
| 	//filename := filepath.Join(tmpDir, "test.log")
 | ||||
| 
 | ||||
| 	fileLogger := NewFileLogger() | ||||
| 	//realFileLogger, ok := fileLogger.(*FileLogger)
 | ||||
| 	//assert.Equal(t, true, ok)
 | ||||
| 
 | ||||
| 	// Fail if there is bad json
 | ||||
| 	err = fileLogger.Init("{") | ||||
| 	assert.Error(t, err) | ||||
| 
 | ||||
| 	// Fail if there is no filename
 | ||||
| 	err = fileLogger.Init(fmt.Sprintf("{\"prefix\":\"%s\",\"level\":\"%s\",\"flags\":%d,\"filename\":\"%s\"}", prefix, level.String(), flags, "")) | ||||
| 	assert.Error(t, err) | ||||
| 
 | ||||
| 	// Fail if the file isn't a filename
 | ||||
| 	err = fileLogger.Init(fmt.Sprintf("{\"prefix\":\"%s\",\"level\":\"%s\",\"flags\":%d,\"filename\":\"%s\"}", prefix, level.String(), flags, filepath.ToSlash(tmpDir))) | ||||
| 	assert.Error(t, err) | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| func TestFileLogger(t *testing.T) { | ||||
| 	tmpDir, err := ioutil.TempDir("", "TestFileLogger") | ||||
| 	assert.NoError(t, err) | ||||
| 	defer os.RemoveAll(tmpDir) | ||||
| 
 | ||||
| 	prefix := "TestPrefix " | ||||
| 	level := INFO | ||||
| 	flags := LstdFlags | LUTC | Lfuncname | ||||
| 	filename := filepath.Join(tmpDir, "test.log") | ||||
| 
 | ||||
| 	fileLogger := NewFileLogger() | ||||
| 	realFileLogger, ok := fileLogger.(*FileLogger) | ||||
| 	assert.Equal(t, true, ok) | ||||
| 
 | ||||
| 	location, _ := time.LoadLocation("EST") | ||||
| 
 | ||||
| 	date := time.Date(2019, time.January, 13, 22, 3, 30, 15, location) | ||||
| 
 | ||||
| 	dateString := date.UTC().Format("2006/01/02 15:04:05") | ||||
| 
 | ||||
| 	event := Event{ | ||||
| 		level:    INFO, | ||||
| 		msg:      "TEST MSG", | ||||
| 		caller:   "CALLER", | ||||
| 		filename: "FULL/FILENAME", | ||||
| 		line:     1, | ||||
| 		time:     date, | ||||
| 	} | ||||
| 
 | ||||
| 	expected := fmt.Sprintf("%s%s %s:%d:%s [%c] %s\n", prefix, dateString, event.filename, event.line, event.caller, strings.ToUpper(event.level.String())[0], event.msg) | ||||
| 
 | ||||
| 	fileLogger.Init(fmt.Sprintf("{\"prefix\":\"%s\",\"level\":\"%s\",\"flags\":%d,\"filename\":\"%s\",\"maxsize\":%d,\"compress\":false}", prefix, level.String(), flags, filepath.ToSlash(filename), len(expected)*2)) | ||||
| 
 | ||||
| 	assert.Equal(t, flags, realFileLogger.Flags) | ||||
| 	assert.Equal(t, level, realFileLogger.Level) | ||||
| 	assert.Equal(t, level, fileLogger.GetLevel()) | ||||
| 
 | ||||
| 	fileLogger.LogEvent(&event) | ||||
| 	fileLogger.Flush() | ||||
| 	logData, err := ioutil.ReadFile(filename) | ||||
| 	assert.NoError(t, err) | ||||
| 	assert.Equal(t, expected, string(logData)) | ||||
| 
 | ||||
| 	event.level = DEBUG | ||||
| 	expected = expected + "" | ||||
| 	fileLogger.LogEvent(&event) | ||||
| 	fileLogger.Flush() | ||||
| 	logData, err = ioutil.ReadFile(filename) | ||||
| 	assert.NoError(t, err) | ||||
| 	assert.Equal(t, expected, string(logData)) | ||||
| 
 | ||||
| 	event.level = TRACE | ||||
| 	expected = expected + "" | ||||
| 	fileLogger.LogEvent(&event) | ||||
| 	fileLogger.Flush() | ||||
| 	logData, err = ioutil.ReadFile(filename) | ||||
| 	assert.NoError(t, err) | ||||
| 	assert.Equal(t, expected, string(logData)) | ||||
| 
 | ||||
| 	event.level = WARN | ||||
| 	expected = expected + fmt.Sprintf("%s%s %s:%d:%s [%c] %s\n", prefix, dateString, event.filename, event.line, event.caller, strings.ToUpper(event.level.String())[0], event.msg) | ||||
| 	fileLogger.LogEvent(&event) | ||||
| 	fileLogger.Flush() | ||||
| 	logData, err = ioutil.ReadFile(filename) | ||||
| 	assert.NoError(t, err) | ||||
| 	assert.Equal(t, expected, string(logData)) | ||||
| 
 | ||||
| 	// Should rotate
 | ||||
| 	fileLogger.LogEvent(&event) | ||||
| 	fileLogger.Flush() | ||||
| 	logData, err = ioutil.ReadFile(filename + fmt.Sprintf(".%s.%03d", time.Now().Format("2006-01-02"), 1)) | ||||
| 	assert.NoError(t, err) | ||||
| 	assert.Equal(t, expected, string(logData)) | ||||
| 
 | ||||
| 	logData, err = ioutil.ReadFile(filename) | ||||
| 	assert.NoError(t, err) | ||||
| 	expected = fmt.Sprintf("%s%s %s:%d:%s [%c] %s\n", prefix, dateString, event.filename, event.line, event.caller, strings.ToUpper(event.level.String())[0], event.msg) | ||||
| 	assert.Equal(t, expected, string(logData)) | ||||
| 
 | ||||
| 	for num := 2; num <= 999; num++ { | ||||
| 		file, err := os.OpenFile(filename+fmt.Sprintf(".%s.%03d", time.Now().Format("2006-01-02"), num), os.O_RDONLY|os.O_CREATE, 0666) | ||||
| 		assert.NoError(t, err) | ||||
| 		file.Close() | ||||
| 	} | ||||
| 	err = realFileLogger.DoRotate() | ||||
| 	assert.Error(t, err) | ||||
| 
 | ||||
| 	expected = expected + fmt.Sprintf("%s%s %s:%d:%s [%c] %s\n", prefix, dateString, event.filename, event.line, event.caller, strings.ToUpper(event.level.String())[0], event.msg) | ||||
| 	fileLogger.LogEvent(&event) | ||||
| 	fileLogger.Flush() | ||||
| 	logData, err = ioutil.ReadFile(filename) | ||||
| 	assert.NoError(t, err) | ||||
| 	assert.Equal(t, expected, string(logData)) | ||||
| 
 | ||||
| 	// Should fail to rotate
 | ||||
| 	expected = expected + fmt.Sprintf("%s%s %s:%d:%s [%c] %s\n", prefix, dateString, event.filename, event.line, event.caller, strings.ToUpper(event.level.String())[0], event.msg) | ||||
| 	fileLogger.LogEvent(&event) | ||||
| 	fileLogger.Flush() | ||||
| 	logData, err = ioutil.ReadFile(filename) | ||||
| 	assert.NoError(t, err) | ||||
| 	assert.Equal(t, expected, string(logData)) | ||||
| 
 | ||||
| 	fileLogger.Close() | ||||
| } | ||||
| 
 | ||||
| func TestCompressFileLogger(t *testing.T) { | ||||
| 	tmpDir, err := ioutil.TempDir("", "TestFileLogger") | ||||
| 	assert.NoError(t, err) | ||||
| 	defer os.RemoveAll(tmpDir) | ||||
| 
 | ||||
| 	prefix := "TestPrefix " | ||||
| 	level := INFO | ||||
| 	flags := LstdFlags | LUTC | Lfuncname | ||||
| 	filename := filepath.Join(tmpDir, "test.log") | ||||
| 
 | ||||
| 	fileLogger := NewFileLogger() | ||||
| 	realFileLogger, ok := fileLogger.(*FileLogger) | ||||
| 	assert.Equal(t, true, ok) | ||||
| 
 | ||||
| 	location, _ := time.LoadLocation("EST") | ||||
| 
 | ||||
| 	date := time.Date(2019, time.January, 13, 22, 3, 30, 15, location) | ||||
| 
 | ||||
| 	dateString := date.UTC().Format("2006/01/02 15:04:05") | ||||
| 
 | ||||
| 	event := Event{ | ||||
| 		level:    INFO, | ||||
| 		msg:      "TEST MSG", | ||||
| 		caller:   "CALLER", | ||||
| 		filename: "FULL/FILENAME", | ||||
| 		line:     1, | ||||
| 		time:     date, | ||||
| 	} | ||||
| 
 | ||||
| 	expected := fmt.Sprintf("%s%s %s:%d:%s [%c] %s\n", prefix, dateString, event.filename, event.line, event.caller, strings.ToUpper(event.level.String())[0], event.msg) | ||||
| 
 | ||||
| 	fileLogger.Init(fmt.Sprintf("{\"prefix\":\"%s\",\"level\":\"%s\",\"flags\":%d,\"filename\":\"%s\",\"maxsize\":%d,\"compress\":true}", prefix, level.String(), flags, filepath.ToSlash(filename), len(expected)*2)) | ||||
| 
 | ||||
| 	fileLogger.LogEvent(&event) | ||||
| 	fileLogger.Flush() | ||||
| 	logData, err := ioutil.ReadFile(filename) | ||||
| 	assert.NoError(t, err) | ||||
| 	assert.Equal(t, expected, string(logData)) | ||||
| 
 | ||||
| 	event.level = WARN | ||||
| 	expected = expected + fmt.Sprintf("%s%s %s:%d:%s [%c] %s\n", prefix, dateString, event.filename, event.line, event.caller, strings.ToUpper(event.level.String())[0], event.msg) | ||||
| 	fileLogger.LogEvent(&event) | ||||
| 	fileLogger.Flush() | ||||
| 	logData, err = ioutil.ReadFile(filename) | ||||
| 	assert.NoError(t, err) | ||||
| 	assert.Equal(t, expected, string(logData)) | ||||
| 
 | ||||
| 	// Should rotate
 | ||||
| 	fileLogger.LogEvent(&event) | ||||
| 	fileLogger.Flush() | ||||
| 
 | ||||
| 	for num := 2; num <= 999; num++ { | ||||
| 		file, err := os.OpenFile(filename+fmt.Sprintf(".%s.%03d.gz", time.Now().Format("2006-01-02"), num), os.O_RDONLY|os.O_CREATE, 0666) | ||||
| 		assert.NoError(t, err) | ||||
| 		file.Close() | ||||
| 	} | ||||
| 	err = realFileLogger.DoRotate() | ||||
| 	assert.Error(t, err) | ||||
| } | ||||
| 
 | ||||
| func TestCompressOldFile(t *testing.T) { | ||||
| 	tmpDir, err := ioutil.TempDir("", "TestFileLogger") | ||||
| 	assert.NoError(t, err) | ||||
| 	defer os.RemoveAll(tmpDir) | ||||
| 	fname := filepath.Join(tmpDir, "test") | ||||
| 	nonGzip := filepath.Join(tmpDir, "test-nonGzip") | ||||
| 
 | ||||
| 	f, err := os.OpenFile(fname, os.O_CREATE|os.O_WRONLY, 0660) | ||||
| 	assert.NoError(t, err) | ||||
| 	ng, err := os.OpenFile(nonGzip, os.O_CREATE|os.O_WRONLY, 0660) | ||||
| 	assert.NoError(t, err) | ||||
| 
 | ||||
| 	for i := 0; i < 999; i++ { | ||||
| 		f.WriteString("This is a test file\n") | ||||
| 		ng.WriteString("This is a test file\n") | ||||
| 	} | ||||
| 	f.Close() | ||||
| 	ng.Close() | ||||
| 
 | ||||
| 	err = compressOldLogFile(fname, -1) | ||||
| 	assert.NoError(t, err) | ||||
| 
 | ||||
| 	_, err = os.Lstat(fname + ".gz") | ||||
| 	assert.NoError(t, err) | ||||
| 
 | ||||
| 	f, err = os.Open(fname + ".gz") | ||||
| 	assert.NoError(t, err) | ||||
| 	zr, err := gzip.NewReader(f) | ||||
| 	assert.NoError(t, err) | ||||
| 	data, err := ioutil.ReadAll(zr) | ||||
| 	assert.NoError(t, err) | ||||
| 	original, err := ioutil.ReadFile(nonGzip) | ||||
| 	assert.NoError(t, err) | ||||
| 	assert.Equal(t, original, data) | ||||
| } | ||||
							
								
								
									
										111
									
								
								modules/log/level.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								modules/log/level.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,111 @@ | |||
| // 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" | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| 	"strings" | ||||
| ) | ||||
| 
 | ||||
| // Level is the level of the logger
 | ||||
| type Level int | ||||
| 
 | ||||
| const ( | ||||
| 	// TRACE represents the lowest log level
 | ||||
| 	TRACE Level = iota | ||||
| 	// DEBUG is for debug logging
 | ||||
| 	DEBUG | ||||
| 	// INFO is for information
 | ||||
| 	INFO | ||||
| 	// WARN is for warning information
 | ||||
| 	WARN | ||||
| 	// ERROR is for error reporting
 | ||||
| 	ERROR | ||||
| 	// CRITICAL is for critical errors
 | ||||
| 	CRITICAL | ||||
| 	// FATAL is for fatal errors
 | ||||
| 	FATAL | ||||
| 	// NONE is for no logging
 | ||||
| 	NONE | ||||
| ) | ||||
| 
 | ||||
| var toString = map[Level]string{ | ||||
| 	TRACE:    "trace", | ||||
| 	DEBUG:    "debug", | ||||
| 	INFO:     "info", | ||||
| 	WARN:     "warn", | ||||
| 	ERROR:    "error", | ||||
| 	CRITICAL: "critical", | ||||
| 	FATAL:    "fatal", | ||||
| 	NONE:     "none", | ||||
| } | ||||
| 
 | ||||
| var toLevel = map[string]Level{ | ||||
| 	"trace":    TRACE, | ||||
| 	"debug":    DEBUG, | ||||
| 	"info":     INFO, | ||||
| 	"warn":     WARN, | ||||
| 	"error":    ERROR, | ||||
| 	"critical": CRITICAL, | ||||
| 	"fatal":    FATAL, | ||||
| 	"none":     NONE, | ||||
| } | ||||
| 
 | ||||
| // Levels returns all the possible logging levels
 | ||||
| func Levels() []string { | ||||
| 	keys := make([]string, 0) | ||||
| 	for key := range toLevel { | ||||
| 		keys = append(keys, key) | ||||
| 	} | ||||
| 	return keys | ||||
| } | ||||
| 
 | ||||
| func (l Level) String() string { | ||||
| 	s, ok := toString[l] | ||||
| 	if ok { | ||||
| 		return s | ||||
| 	} | ||||
| 	return "info" | ||||
| } | ||||
| 
 | ||||
| // MarshalJSON takes a Level and turns it into text
 | ||||
| func (l Level) MarshalJSON() ([]byte, error) { | ||||
| 	buffer := bytes.NewBufferString(`"`) | ||||
| 	buffer.WriteString(toString[l]) | ||||
| 	buffer.WriteString(`"`) | ||||
| 	return buffer.Bytes(), nil | ||||
| } | ||||
| 
 | ||||
| // FromString takes a level string and returns a Level
 | ||||
| func FromString(level string) Level { | ||||
| 	temp, ok := toLevel[strings.ToLower(level)] | ||||
| 	if !ok { | ||||
| 		return INFO | ||||
| 	} | ||||
| 	return temp | ||||
| } | ||||
| 
 | ||||
| // UnmarshalJSON takes text and turns it into a Level
 | ||||
| func (l *Level) UnmarshalJSON(b []byte) error { | ||||
| 	var tmp interface{} | ||||
| 	err := json.Unmarshal(b, &tmp) | ||||
| 	if err != nil { | ||||
| 		fmt.Fprintf(os.Stderr, "Err: %v", err) | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	switch v := tmp.(type) { | ||||
| 	case string: | ||||
| 		*l = FromString(string(v)) | ||||
| 	case int: | ||||
| 		*l = FromString(Level(v).String()) | ||||
| 	default: | ||||
| 		*l = INFO | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										55
									
								
								modules/log/level_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								modules/log/level_test.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,55 @@ | |||
| // 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 ( | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"testing" | ||||
| 
 | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| ) | ||||
| 
 | ||||
| type testLevel struct { | ||||
| 	Level Level `json:"level"` | ||||
| } | ||||
| 
 | ||||
| func TestLevelMarshalUnmarshalJSON(t *testing.T) { | ||||
| 	levelBytes, err := json.Marshal(testLevel{ | ||||
| 		Level: INFO, | ||||
| 	}) | ||||
| 	assert.NoError(t, err) | ||||
| 	assert.Equal(t, string(makeTestLevelBytes(INFO.String())), string(levelBytes)) | ||||
| 
 | ||||
| 	var testLevel testLevel | ||||
| 	err = json.Unmarshal(levelBytes, &testLevel) | ||||
| 	assert.NoError(t, err) | ||||
| 	assert.Equal(t, INFO, testLevel.Level) | ||||
| 
 | ||||
| 	err = json.Unmarshal(makeTestLevelBytes(`FOFOO`), &testLevel) | ||||
| 	assert.NoError(t, err) | ||||
| 	assert.Equal(t, INFO, testLevel.Level) | ||||
| 
 | ||||
| 	err = json.Unmarshal([]byte(fmt.Sprintf(`{"level":%d}`, 2)), &testLevel) | ||||
| 	assert.NoError(t, err) | ||||
| 	assert.Equal(t, INFO, testLevel.Level) | ||||
| 
 | ||||
| 	err = json.Unmarshal([]byte(fmt.Sprintf(`{"level":%d}`, 10012)), &testLevel) | ||||
| 	assert.NoError(t, err) | ||||
| 	assert.Equal(t, INFO, testLevel.Level) | ||||
| 
 | ||||
| 	err = json.Unmarshal([]byte(`{"level":{}}`), &testLevel) | ||||
| 	assert.NoError(t, err) | ||||
| 	assert.Equal(t, INFO, testLevel.Level) | ||||
| 
 | ||||
| 	assert.Equal(t, INFO.String(), Level(1001).String()) | ||||
| 
 | ||||
| 	err = json.Unmarshal([]byte(`{"level":{}`), &testLevel.Level) | ||||
| 	assert.Error(t, err) | ||||
| } | ||||
| 
 | ||||
| func makeTestLevelBytes(level string) []byte { | ||||
| 	return []byte(fmt.Sprintf(`{"level":"%s"}`, level)) | ||||
| } | ||||
|  | @ -8,48 +8,68 @@ import ( | |||
| 	"fmt" | ||||
| 	"os" | ||||
| 	"path" | ||||
| 	"path/filepath" | ||||
| 	"runtime" | ||||
| 	"strings" | ||||
| 
 | ||||
| 	"golang.org/x/sync/syncmap" | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| 	loggers []*Logger | ||||
| 	// DEFAULT is the name of the default logger
 | ||||
| 	DEFAULT = "default" | ||||
| 	// NamedLoggers map of named loggers
 | ||||
| 	NamedLoggers = make(map[string]*Logger) | ||||
| 	// GitLogger logger for git
 | ||||
| 	GitLogger *Logger | ||||
| 	prefix    string | ||||
| ) | ||||
| 
 | ||||
| // NewLogger create a logger
 | ||||
| func NewLogger(bufLen int64, mode, config string) { | ||||
| 	logger := newLogger(bufLen) | ||||
| // NewLogger create a logger for the default logger
 | ||||
| func NewLogger(bufLen int64, name, provider, config string) *Logger { | ||||
| 	err := NewNamedLogger(DEFAULT, bufLen, name, provider, config) | ||||
| 	if err != nil { | ||||
| 		CriticalWithSkip(1, "Unable to create default logger: %v", err) | ||||
| 		panic(err) | ||||
| 	} | ||||
| 	return NamedLoggers[DEFAULT] | ||||
| } | ||||
| 
 | ||||
| 	isExist := false | ||||
| 	for i, l := range loggers { | ||||
| 		if l.adapter == mode { | ||||
| 			isExist = true | ||||
| 			loggers[i] = logger | ||||
| 		} | ||||
| // NewNamedLogger creates a new named logger for a given configuration
 | ||||
| func NewNamedLogger(name string, bufLen int64, subname, provider, config string) error { | ||||
| 	logger, ok := NamedLoggers[name] | ||||
| 	if !ok { | ||||
| 		logger = newLogger(name, bufLen) | ||||
| 
 | ||||
| 		NamedLoggers[name] = logger | ||||
| 	} | ||||
| 	if !isExist { | ||||
| 		loggers = append(loggers, logger) | ||||
| 	} | ||||
| 	if err := logger.SetLogger(mode, config); err != nil { | ||||
| 		Fatal(2, "Failed to set logger (%s): %v", mode, err) | ||||
| 
 | ||||
| 	return logger.SetLogger(subname, provider, config) | ||||
| } | ||||
| 
 | ||||
| // DelNamedLogger closes and deletes the named logger
 | ||||
| func DelNamedLogger(name string) { | ||||
| 	l, ok := NamedLoggers[name] | ||||
| 	if ok { | ||||
| 		delete(NamedLoggers, name) | ||||
| 		l.Close() | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // DelLogger removes loggers that are for the given mode
 | ||||
| func DelLogger(mode string) error { | ||||
| 	for _, l := range loggers { | ||||
| 		if _, ok := l.outputs.Load(mode); ok { | ||||
| 			return l.DelLogger(mode) | ||||
| 		} | ||||
| // DelLogger removes the named sublogger from the default logger
 | ||||
| func DelLogger(name string) error { | ||||
| 	logger := NamedLoggers[DEFAULT] | ||||
| 	found, err := logger.DelLogger(name) | ||||
| 	if !found { | ||||
| 		Trace("Log %s not found, no need to delete", name) | ||||
| 	} | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| 	Trace("Log adapter %s not found, no need to delete", mode) | ||||
| 	return nil | ||||
| // GetLogger returns either a named logger or the default logger
 | ||||
| func GetLogger(name string) *Logger { | ||||
| 	logger, ok := NamedLoggers[name] | ||||
| 	if ok { | ||||
| 		return logger | ||||
| 	} | ||||
| 	return NamedLoggers[DEFAULT] | ||||
| } | ||||
| 
 | ||||
| // NewGitLogger create a logger for git
 | ||||
|  | @ -58,333 +78,168 @@ func NewGitLogger(logPath string) { | |||
| 	path := path.Dir(logPath) | ||||
| 
 | ||||
| 	if err := os.MkdirAll(path, os.ModePerm); err != nil { | ||||
| 		Fatal(4, "Failed to create dir %s: %v", path, err) | ||||
| 		Fatal("Failed to create dir %s: %v", path, err) | ||||
| 	} | ||||
| 
 | ||||
| 	GitLogger = newLogger(0) | ||||
| 	GitLogger.SetLogger("file", fmt.Sprintf(`{"level":0,"filename":"%s","rotate":false}`, logPath)) | ||||
| 	GitLogger = newLogger("git", 0) | ||||
| 	GitLogger.SetLogger("file", "file", fmt.Sprintf(`{"level":"TRACE","filename":"%s","rotate":false}`, logPath)) | ||||
| } | ||||
| 
 | ||||
| // GetLevel returns the minimum logger level
 | ||||
| func GetLevel() Level { | ||||
| 	return NamedLoggers[DEFAULT].GetLevel() | ||||
| } | ||||
| 
 | ||||
| // GetStacktraceLevel returns the minimum logger level
 | ||||
| func GetStacktraceLevel() Level { | ||||
| 	return NamedLoggers[DEFAULT].GetStacktraceLevel() | ||||
| } | ||||
| 
 | ||||
| // Trace records trace log
 | ||||
| func Trace(format string, v ...interface{}) { | ||||
| 	for _, logger := range loggers { | ||||
| 		logger.Trace(format, v...) | ||||
| 	} | ||||
| 	Log(1, TRACE, format, v...) | ||||
| } | ||||
| 
 | ||||
| // IsTrace returns true if at least one logger is TRACE
 | ||||
| func IsTrace() bool { | ||||
| 	return GetLevel() <= TRACE | ||||
| } | ||||
| 
 | ||||
| // Debug records debug log
 | ||||
| func Debug(format string, v ...interface{}) { | ||||
| 	for _, logger := range loggers { | ||||
| 		logger.Debug(format, v...) | ||||
| 	} | ||||
| 	Log(1, DEBUG, format, v...) | ||||
| } | ||||
| 
 | ||||
| // IsDebug returns true if at least one logger is DEBUG
 | ||||
| func IsDebug() bool { | ||||
| 	return GetLevel() <= DEBUG | ||||
| } | ||||
| 
 | ||||
| // Info records info log
 | ||||
| func Info(format string, v ...interface{}) { | ||||
| 	for _, logger := range loggers { | ||||
| 		logger.Info(format, v...) | ||||
| 	} | ||||
| 	Log(1, INFO, format, v...) | ||||
| } | ||||
| 
 | ||||
| // IsInfo returns true if at least one logger is INFO
 | ||||
| func IsInfo() bool { | ||||
| 	return GetLevel() <= INFO | ||||
| } | ||||
| 
 | ||||
| // Warn records warning log
 | ||||
| func Warn(format string, v ...interface{}) { | ||||
| 	for _, logger := range loggers { | ||||
| 		logger.Warn(format, v...) | ||||
| 	} | ||||
| 	Log(1, WARN, format, v...) | ||||
| } | ||||
| 
 | ||||
| // IsWarn returns true if at least one logger is WARN
 | ||||
| func IsWarn() bool { | ||||
| 	return GetLevel() <= WARN | ||||
| } | ||||
| 
 | ||||
| // Error records error log
 | ||||
| func Error(skip int, format string, v ...interface{}) { | ||||
| 	for _, logger := range loggers { | ||||
| 		logger.Error(skip, format, v...) | ||||
| 	} | ||||
| func Error(format string, v ...interface{}) { | ||||
| 	Log(1, ERROR, format, v...) | ||||
| } | ||||
| 
 | ||||
| // ErrorWithSkip records error log from "skip" calls back from this function
 | ||||
| func ErrorWithSkip(skip int, format string, v ...interface{}) { | ||||
| 	Log(skip+1, ERROR, format, v...) | ||||
| } | ||||
| 
 | ||||
| // IsError returns true if at least one logger is ERROR
 | ||||
| func IsError() bool { | ||||
| 	return GetLevel() <= ERROR | ||||
| } | ||||
| 
 | ||||
| // Critical records critical log
 | ||||
| func Critical(skip int, format string, v ...interface{}) { | ||||
| 	for _, logger := range loggers { | ||||
| 		logger.Critical(skip, format, v...) | ||||
| 	} | ||||
| func Critical(format string, v ...interface{}) { | ||||
| 	Log(1, CRITICAL, format, v...) | ||||
| } | ||||
| 
 | ||||
| // Fatal records error log and exit process
 | ||||
| func Fatal(skip int, format string, v ...interface{}) { | ||||
| 	Error(skip, format, v...) | ||||
| 	for _, l := range loggers { | ||||
| 		l.Close() | ||||
| 	} | ||||
| // CriticalWithSkip records critical log from "skip" calls back from this function
 | ||||
| func CriticalWithSkip(skip int, format string, v ...interface{}) { | ||||
| 	Log(skip+1, CRITICAL, format, v...) | ||||
| } | ||||
| 
 | ||||
| // IsCritical returns true if at least one logger is CRITICAL
 | ||||
| func IsCritical() bool { | ||||
| 	return GetLevel() <= CRITICAL | ||||
| } | ||||
| 
 | ||||
| // Fatal records fatal log and exit process
 | ||||
| func Fatal(format string, v ...interface{}) { | ||||
| 	Log(1, FATAL, format, v...) | ||||
| 	Close() | ||||
| 	os.Exit(1) | ||||
| } | ||||
| 
 | ||||
| // FatalWithSkip records fatal log from "skip" calls back from this function
 | ||||
| func FatalWithSkip(skip int, format string, v ...interface{}) { | ||||
| 	Log(skip+1, FATAL, format, v...) | ||||
| 	Close() | ||||
| 	os.Exit(1) | ||||
| } | ||||
| 
 | ||||
| // IsFatal returns true if at least one logger is FATAL
 | ||||
| func IsFatal() bool { | ||||
| 	return GetLevel() <= FATAL | ||||
| } | ||||
| 
 | ||||
| // Close closes all the loggers
 | ||||
| func Close() { | ||||
| 	for _, l := range loggers { | ||||
| 		l.Close() | ||||
| 	l, ok := NamedLoggers[DEFAULT] | ||||
| 	if !ok { | ||||
| 		return | ||||
| 	} | ||||
| 	delete(NamedLoggers, DEFAULT) | ||||
| 	l.Close() | ||||
| } | ||||
| 
 | ||||
| // Log a message with defined skip and at logging level
 | ||||
| // A skip of 0 refers to the caller of this command
 | ||||
| func Log(skip int, level Level, format string, v ...interface{}) { | ||||
| 	l, ok := NamedLoggers[DEFAULT] | ||||
| 	if ok { | ||||
| 		l.Log(skip+1, level, format, v...) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // .___        __                 _____
 | ||||
| // |   | _____/  |_  ____________/ ____\____    ____  ____
 | ||||
| // |   |/    \   __\/ __ \_  __ \   __\\__  \ _/ ___\/ __ \
 | ||||
| // |   |   |  \  | \  ___/|  | \/|  |   / __ \\  \__\  ___/
 | ||||
| // |___|___|  /__|  \___  >__|   |__|  (____  /\___  >___  >
 | ||||
| //          \/          \/                  \/     \/    \/
 | ||||
| 
 | ||||
| // LogLevel level type for log
 | ||||
| //type LogLevel int
 | ||||
| 
 | ||||
| // log levels
 | ||||
| const ( | ||||
| 	TRACE = iota | ||||
| 	DEBUG | ||||
| 	INFO | ||||
| 	WARN | ||||
| 	ERROR | ||||
| 	CRITICAL | ||||
| 	FATAL | ||||
| ) | ||||
| 
 | ||||
| // LoggerInterface represents behaviors of a logger provider.
 | ||||
| type LoggerInterface interface { | ||||
| 	Init(config string) error | ||||
| 	WriteMsg(msg string, skip, level int) error | ||||
| 	Destroy() | ||||
| 	Flush() | ||||
| } | ||||
| 
 | ||||
| type loggerType func() LoggerInterface | ||||
| 
 | ||||
| // LoggerAsWriter is a io.Writer shim around the gitea log
 | ||||
| type LoggerAsWriter struct { | ||||
| 	level int | ||||
| 	ourLoggers []*Logger | ||||
| 	level      Level | ||||
| } | ||||
| 
 | ||||
| // NewLoggerAsWriter creates a Writer representation of the logger with setable log level
 | ||||
| func NewLoggerAsWriter(level string) *LoggerAsWriter { | ||||
| 	l := &LoggerAsWriter{} | ||||
| 	switch strings.ToUpper(level) { | ||||
| 	case "TRACE": | ||||
| 		l.level = TRACE | ||||
| 	case "DEBUG": | ||||
| 		l.level = DEBUG | ||||
| 	case "INFO": | ||||
| 		l.level = INFO | ||||
| 	case "WARN": | ||||
| 		l.level = WARN | ||||
| 	case "ERROR": | ||||
| 		l.level = ERROR | ||||
| 	case "CRITICAL": | ||||
| 		l.level = CRITICAL | ||||
| 	case "FATAL": | ||||
| 		l.level = FATAL | ||||
| 	default: | ||||
| 		l.level = INFO | ||||
| func NewLoggerAsWriter(level string, ourLoggers ...*Logger) *LoggerAsWriter { | ||||
| 	if len(ourLoggers) == 0 { | ||||
| 		ourLoggers = []*Logger{NamedLoggers[DEFAULT]} | ||||
| 	} | ||||
| 	l := &LoggerAsWriter{ | ||||
| 		ourLoggers: ourLoggers, | ||||
| 		level:      FromString(level), | ||||
| 	} | ||||
| 	return l | ||||
| } | ||||
| 
 | ||||
| // Write implements the io.Writer interface to allow spoofing of macaron
 | ||||
| func (l *LoggerAsWriter) Write(p []byte) (int, error) { | ||||
| 	l.Log(string(p)) | ||||
| 	for _, logger := range l.ourLoggers { | ||||
| 		// Skip = 3 because this presumes that we have been called by log.Println()
 | ||||
| 		// If the caller has used log.Output or the like this will be wrong
 | ||||
| 		logger.Log(3, l.level, string(p)) | ||||
| 	} | ||||
| 	return len(p), nil | ||||
| } | ||||
| 
 | ||||
| // Log takes a given string and logs it at the set log-level
 | ||||
| func (l *LoggerAsWriter) Log(msg string) { | ||||
| 	for _, logger := range loggers { | ||||
| 		logger.writerMsg(0, l.level, msg) | ||||
| 	for _, logger := range l.ourLoggers { | ||||
| 		// Set the skip to reference the call just above this
 | ||||
| 		logger.Log(1, l.level, msg) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| var adapters = make(map[string]loggerType) | ||||
| 
 | ||||
| // Register registers given logger provider to adapters.
 | ||||
| func Register(name string, log loggerType) { | ||||
| 	if log == nil { | ||||
| 		panic("log: register provider is nil") | ||||
| 	} | ||||
| 	if _, dup := adapters[name]; dup { | ||||
| 		panic("log: register called twice for provider \"" + name + "\"") | ||||
| 	} | ||||
| 	adapters[name] = log | ||||
| } | ||||
| 
 | ||||
| type logMsg struct { | ||||
| 	skip, level int | ||||
| 	msg         string | ||||
| } | ||||
| 
 | ||||
| // Logger is default logger in beego application.
 | ||||
| // it can contain several providers and log message into all providers.
 | ||||
| type Logger struct { | ||||
| 	adapter string | ||||
| 	level   int | ||||
| 	msg     chan *logMsg | ||||
| 	outputs syncmap.Map | ||||
| 	quit    chan bool | ||||
| } | ||||
| 
 | ||||
| // newLogger initializes and returns a new logger.
 | ||||
| func newLogger(buffer int64) *Logger { | ||||
| 	l := &Logger{ | ||||
| 		msg:  make(chan *logMsg, buffer), | ||||
| 		quit: make(chan bool), | ||||
| 	} | ||||
| 	go l.StartLogger() | ||||
| 	return l | ||||
| } | ||||
| 
 | ||||
| // SetLogger sets new logger instance with given logger adapter and config.
 | ||||
| func (l *Logger) SetLogger(adapter string, config string) error { | ||||
| 	if log, ok := adapters[adapter]; ok { | ||||
| 		lg := log() | ||||
| 		if err := lg.Init(config); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		l.outputs.Store(adapter, lg) | ||||
| 		l.adapter = adapter | ||||
| 	} else { | ||||
| 		panic("log: unknown adapter \"" + adapter + "\" (forgotten register?)") | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // DelLogger removes a logger adapter instance.
 | ||||
| func (l *Logger) DelLogger(adapter string) error { | ||||
| 	if lg, ok := l.outputs.Load(adapter); ok { | ||||
| 		lg.(LoggerInterface).Destroy() | ||||
| 		l.outputs.Delete(adapter) | ||||
| 	} else { | ||||
| 		panic("log: unknown adapter \"" + adapter + "\" (forgotten register?)") | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (l *Logger) writerMsg(skip, level int, msg string) error { | ||||
| 	if l.level > level { | ||||
| 		return nil | ||||
| 	} | ||||
| 	lm := &logMsg{ | ||||
| 		skip:  skip, | ||||
| 		level: level, | ||||
| 	} | ||||
| 
 | ||||
| 	// Only error information needs locate position for debugging.
 | ||||
| 	if lm.level >= ERROR { | ||||
| 		pc, file, line, ok := runtime.Caller(skip) | ||||
| 		if ok { | ||||
| 			// Get caller function name.
 | ||||
| 			fn := runtime.FuncForPC(pc) | ||||
| 			var fnName string | ||||
| 			if fn == nil { | ||||
| 				fnName = "?()" | ||||
| 			} else { | ||||
| 				fnName = strings.TrimLeft(filepath.Ext(fn.Name()), ".") + "()" | ||||
| 			} | ||||
| 
 | ||||
| 			fileName := file | ||||
| 			if len(fileName) > 20 { | ||||
| 				fileName = "..." + fileName[len(fileName)-20:] | ||||
| 			} | ||||
| 			lm.msg = fmt.Sprintf("[%s:%d %s] %s", fileName, line, fnName, msg) | ||||
| 		} else { | ||||
| 			lm.msg = msg | ||||
| 		} | ||||
| 	} else { | ||||
| 		lm.msg = msg | ||||
| 	} | ||||
| 	l.msg <- lm | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // StartLogger starts logger chan reading.
 | ||||
| func (l *Logger) StartLogger() { | ||||
| 	for { | ||||
| 		select { | ||||
| 		case bm := <-l.msg: | ||||
| 			l.outputs.Range(func(k, v interface{}) bool { | ||||
| 				if err := v.(LoggerInterface).WriteMsg(bm.msg, bm.skip, bm.level); err != nil { | ||||
| 					fmt.Println("ERROR, unable to WriteMsg:", err) | ||||
| 				} | ||||
| 				return true | ||||
| 			}) | ||||
| 		case <-l.quit: | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Flush flushes all chan data.
 | ||||
| func (l *Logger) Flush() { | ||||
| 	l.outputs.Range(func(k, v interface{}) bool { | ||||
| 		v.(LoggerInterface).Flush() | ||||
| 		return true | ||||
| 	}) | ||||
| } | ||||
| 
 | ||||
| // Close closes logger, flush all chan data and destroy all adapter instances.
 | ||||
| func (l *Logger) Close() { | ||||
| 	l.quit <- true | ||||
| 	for { | ||||
| 		if len(l.msg) > 0 { | ||||
| 			bm := <-l.msg | ||||
| 			l.outputs.Range(func(k, v interface{}) bool { | ||||
| 				if err := v.(LoggerInterface).WriteMsg(bm.msg, bm.skip, bm.level); err != nil { | ||||
| 					fmt.Println("ERROR, unable to WriteMsg:", err) | ||||
| 				} | ||||
| 				return true | ||||
| 			}) | ||||
| 		} else { | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| 	l.outputs.Range(func(k, v interface{}) bool { | ||||
| 		v.(LoggerInterface).Flush() | ||||
| 		v.(LoggerInterface).Destroy() | ||||
| 		return true | ||||
| 	}) | ||||
| } | ||||
| 
 | ||||
| // Trace records trace log
 | ||||
| func (l *Logger) Trace(format string, v ...interface{}) { | ||||
| 	msg := fmt.Sprintf("[T] "+format, v...) | ||||
| 	l.writerMsg(0, TRACE, msg) | ||||
| } | ||||
| 
 | ||||
| // Debug records debug log
 | ||||
| func (l *Logger) Debug(format string, v ...interface{}) { | ||||
| 	msg := fmt.Sprintf("[D] "+format, v...) | ||||
| 	l.writerMsg(0, DEBUG, msg) | ||||
| } | ||||
| 
 | ||||
| // Info records information log
 | ||||
| func (l *Logger) Info(format string, v ...interface{}) { | ||||
| 	msg := fmt.Sprintf("[I] "+format, v...) | ||||
| 	l.writerMsg(0, INFO, msg) | ||||
| } | ||||
| 
 | ||||
| // Warn records warning log
 | ||||
| func (l *Logger) Warn(format string, v ...interface{}) { | ||||
| 	msg := fmt.Sprintf("[W] "+format, v...) | ||||
| 	l.writerMsg(0, WARN, msg) | ||||
| } | ||||
| 
 | ||||
| // Error records error log
 | ||||
| func (l *Logger) Error(skip int, format string, v ...interface{}) { | ||||
| 	msg := fmt.Sprintf("[E] "+format, v...) | ||||
| 	l.writerMsg(skip, ERROR, msg) | ||||
| } | ||||
| 
 | ||||
| // Critical records critical log
 | ||||
| func (l *Logger) Critical(skip int, format string, v ...interface{}) { | ||||
| 	msg := fmt.Sprintf("[C] "+format, v...) | ||||
| 	l.writerMsg(skip, CRITICAL, msg) | ||||
| } | ||||
| 
 | ||||
| // Fatal records error log and exit the process
 | ||||
| func (l *Logger) Fatal(skip int, format string, v ...interface{}) { | ||||
| 	msg := fmt.Sprintf("[F] "+format, v...) | ||||
| 	l.writerMsg(skip, FATAL, msg) | ||||
| 	l.Close() | ||||
| 	os.Exit(1) | ||||
| func init() { | ||||
| 	_, filename, _, _ := runtime.Caller(0) | ||||
| 	prefix = strings.TrimSuffix(filename, "modules/log/log.go") | ||||
| } | ||||
|  |  | |||
							
								
								
									
										154
									
								
								modules/log/log_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										154
									
								
								modules/log/log_test.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,154 @@ | |||
| // 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 ( | ||||
| 	"fmt" | ||||
| 	"testing" | ||||
| 
 | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| ) | ||||
| 
 | ||||
| func baseConsoleTest(t *testing.T, logger *Logger) (chan []byte, chan bool) { | ||||
| 	written := make(chan []byte) | ||||
| 	closed := make(chan bool) | ||||
| 
 | ||||
| 	c := CallbackWriteCloser{ | ||||
| 		callback: func(p []byte, close bool) { | ||||
| 			written <- p | ||||
| 			closed <- close | ||||
| 		}, | ||||
| 	} | ||||
| 	m := logger.MultiChannelledLog | ||||
| 
 | ||||
| 	channelledLog := m.GetEventLogger("console") | ||||
| 	assert.NotEmpty(t, channelledLog) | ||||
| 	realChanLog, ok := channelledLog.(*ChannelledLog) | ||||
| 	assert.Equal(t, true, ok) | ||||
| 	realCL, ok := realChanLog.loggerProvider.(*ConsoleLogger) | ||||
| 	assert.Equal(t, true, ok) | ||||
| 	assert.Equal(t, INFO, realCL.Level) | ||||
| 	realCL.out = c | ||||
| 
 | ||||
| 	format := "test: %s" | ||||
| 	args := []interface{}{"A"} | ||||
| 
 | ||||
| 	logger.Log(0, INFO, format, args...) | ||||
| 	line := <-written | ||||
| 	assert.Contains(t, string(line), fmt.Sprintf(format, args...)) | ||||
| 	assert.Equal(t, false, <-closed) | ||||
| 
 | ||||
| 	format = "test2: %s" | ||||
| 	logger.Warn(format, args...) | ||||
| 	line = <-written | ||||
| 
 | ||||
| 	assert.Contains(t, string(line), fmt.Sprintf(format, args...)) | ||||
| 	assert.Equal(t, false, <-closed) | ||||
| 
 | ||||
| 	format = "testerror: %s" | ||||
| 	logger.Error(format, args...) | ||||
| 	line = <-written | ||||
| 	assert.Contains(t, string(line), fmt.Sprintf(format, args...)) | ||||
| 	assert.Equal(t, false, <-closed) | ||||
| 	return written, closed | ||||
| } | ||||
| 
 | ||||
| func TestNewLoggerUnexported(t *testing.T) { | ||||
| 	level := INFO | ||||
| 	logger := newLogger("UNEXPORTED", 0) | ||||
| 	err := logger.SetLogger("console", "console", fmt.Sprintf(`{"level":"%s"}`, level.String())) | ||||
| 	assert.NoError(t, err) | ||||
| 	out := logger.MultiChannelledLog.GetEventLogger("console") | ||||
| 	assert.NotEmpty(t, out) | ||||
| 	chanlog, ok := out.(*ChannelledLog) | ||||
| 	assert.Equal(t, true, ok) | ||||
| 	assert.Equal(t, "console", chanlog.provider) | ||||
| 	assert.Equal(t, INFO, logger.GetLevel()) | ||||
| 	baseConsoleTest(t, logger) | ||||
| } | ||||
| 
 | ||||
| func TestNewLoggger(t *testing.T) { | ||||
| 	level := INFO | ||||
| 	logger := NewLogger(0, "console", "console", fmt.Sprintf(`{"level":"%s"}`, level.String())) | ||||
| 
 | ||||
| 	assert.Equal(t, INFO, GetLevel()) | ||||
| 	assert.Equal(t, false, IsTrace()) | ||||
| 	assert.Equal(t, false, IsDebug()) | ||||
| 	assert.Equal(t, true, IsInfo()) | ||||
| 	assert.Equal(t, true, IsWarn()) | ||||
| 	assert.Equal(t, true, IsError()) | ||||
| 
 | ||||
| 	written, closed := baseConsoleTest(t, logger) | ||||
| 
 | ||||
| 	format := "test: %s" | ||||
| 	args := []interface{}{"A"} | ||||
| 
 | ||||
| 	Log(0, INFO, format, args...) | ||||
| 	line := <-written | ||||
| 	assert.Contains(t, string(line), fmt.Sprintf(format, args...)) | ||||
| 	assert.Equal(t, false, <-closed) | ||||
| 
 | ||||
| 	Info(format, args...) | ||||
| 	line = <-written | ||||
| 	assert.Contains(t, string(line), fmt.Sprintf(format, args...)) | ||||
| 	assert.Equal(t, false, <-closed) | ||||
| 
 | ||||
| 	go DelLogger("console") | ||||
| 	line = <-written | ||||
| 	assert.Equal(t, "", string(line)) | ||||
| 	assert.Equal(t, true, <-closed) | ||||
| } | ||||
| 
 | ||||
| func TestNewLogggerRecreate(t *testing.T) { | ||||
| 	level := INFO | ||||
| 	NewLogger(0, "console", "console", fmt.Sprintf(`{"level":"%s"}`, level.String())) | ||||
| 
 | ||||
| 	assert.Equal(t, INFO, GetLevel()) | ||||
| 	assert.Equal(t, false, IsTrace()) | ||||
| 	assert.Equal(t, false, IsDebug()) | ||||
| 	assert.Equal(t, true, IsInfo()) | ||||
| 	assert.Equal(t, true, IsWarn()) | ||||
| 	assert.Equal(t, true, IsError()) | ||||
| 
 | ||||
| 	format := "test: %s" | ||||
| 	args := []interface{}{"A"} | ||||
| 
 | ||||
| 	Log(0, INFO, format, args...) | ||||
| 
 | ||||
| 	NewLogger(0, "console", "console", fmt.Sprintf(`{"level":"%s"}`, level.String())) | ||||
| 
 | ||||
| 	assert.Equal(t, INFO, GetLevel()) | ||||
| 	assert.Equal(t, false, IsTrace()) | ||||
| 	assert.Equal(t, false, IsDebug()) | ||||
| 	assert.Equal(t, true, IsInfo()) | ||||
| 	assert.Equal(t, true, IsWarn()) | ||||
| 	assert.Equal(t, true, IsError()) | ||||
| 
 | ||||
| 	Log(0, INFO, format, args...) | ||||
| 
 | ||||
| 	assert.Panics(t, func() { | ||||
| 		NewLogger(0, "console", "console", fmt.Sprintf(`{"level":"%s"`, level.String())) | ||||
| 	}) | ||||
| 
 | ||||
| 	go DelLogger("console") | ||||
| 
 | ||||
| 	// We should be able to redelete without a problem
 | ||||
| 	go DelLogger("console") | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| func TestNewNamedLogger(t *testing.T) { | ||||
| 	level := INFO | ||||
| 	err := NewNamedLogger("test", 0, "console", "console", fmt.Sprintf(`{"level":"%s"}`, level.String())) | ||||
| 	assert.NoError(t, err) | ||||
| 	logger := NamedLoggers["test"] | ||||
| 	assert.Equal(t, level, logger.GetLevel()) | ||||
| 
 | ||||
| 	written, closed := baseConsoleTest(t, logger) | ||||
| 	go DelNamedLogger("test") | ||||
| 	line := <-written | ||||
| 	assert.Equal(t, "", string(line)) | ||||
| 	assert.Equal(t, true, <-closed) | ||||
| } | ||||
							
								
								
									
										156
									
								
								modules/log/logger.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										156
									
								
								modules/log/logger.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,156 @@ | |||
| // 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 ( | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| 	"runtime" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
| // Logger is default logger in the Gitea application.
 | ||||
| // it can contain several providers and log message into all providers.
 | ||||
| type Logger struct { | ||||
| 	*MultiChannelledLog | ||||
| 	bufferLength int64 | ||||
| } | ||||
| 
 | ||||
| // newLogger initializes and returns a new logger.
 | ||||
| func newLogger(name string, buffer int64) *Logger { | ||||
| 	l := &Logger{ | ||||
| 		MultiChannelledLog: NewMultiChannelledLog(name, buffer), | ||||
| 		bufferLength:       buffer, | ||||
| 	} | ||||
| 	return l | ||||
| } | ||||
| 
 | ||||
| // SetLogger sets new logger instance with given logger provider and config.
 | ||||
| func (l *Logger) SetLogger(name, provider, config string) error { | ||||
| 	eventLogger, err := NewChannelledLog(name, provider, config, l.bufferLength) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("Failed to create sublogger (%s): %v", name, err) | ||||
| 	} | ||||
| 
 | ||||
| 	l.MultiChannelledLog.DelLogger(name) | ||||
| 
 | ||||
| 	err = l.MultiChannelledLog.AddLogger(eventLogger) | ||||
| 	if err != nil { | ||||
| 		if IsErrDuplicateName(err) { | ||||
| 			return fmt.Errorf("Duplicate named sublogger %s %v", name, l.MultiChannelledLog.GetEventLoggerNames()) | ||||
| 		} | ||||
| 		return fmt.Errorf("Failed to add sublogger (%s): %v", name, err) | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // DelLogger deletes a sublogger from this logger.
 | ||||
| func (l *Logger) DelLogger(name string) (bool, error) { | ||||
| 	return l.MultiChannelledLog.DelLogger(name), nil | ||||
| } | ||||
| 
 | ||||
| // Log msg at the provided level with the provided caller defined by skip (0 being the function that calls this function)
 | ||||
| func (l *Logger) Log(skip int, level Level, format string, v ...interface{}) error { | ||||
| 	if l.GetLevel() > level { | ||||
| 		return nil | ||||
| 	} | ||||
| 	caller := "?()" | ||||
| 	pc, filename, line, ok := runtime.Caller(skip + 1) | ||||
| 	if ok { | ||||
| 		// Get caller function name.
 | ||||
| 		fn := runtime.FuncForPC(pc) | ||||
| 		if fn != nil { | ||||
| 			caller = fn.Name() + "()" | ||||
| 		} | ||||
| 	} | ||||
| 	msg := format | ||||
| 	if len(v) > 0 { | ||||
| 		args := make([]interface{}, len(v)) | ||||
| 		for i := 0; i < len(args); i++ { | ||||
| 			args[i] = NewColoredValuePointer(&v[i]) | ||||
| 		} | ||||
| 		msg = fmt.Sprintf(format, args...) | ||||
| 	} | ||||
| 	stack := "" | ||||
| 	if l.GetStacktraceLevel() <= level { | ||||
| 		stack = Stack(skip + 1) | ||||
| 	} | ||||
| 	return l.SendLog(level, caller, strings.TrimPrefix(filename, prefix), line, msg, stack) | ||||
| } | ||||
| 
 | ||||
| // SendLog sends a log event at the provided level with the information given
 | ||||
| func (l *Logger) SendLog(level Level, caller, filename string, line int, msg string, stack string) error { | ||||
| 	if l.GetLevel() > level { | ||||
| 		return nil | ||||
| 	} | ||||
| 	event := &Event{ | ||||
| 		level:      level, | ||||
| 		caller:     caller, | ||||
| 		filename:   filename, | ||||
| 		line:       line, | ||||
| 		msg:        msg, | ||||
| 		time:       time.Now(), | ||||
| 		stacktrace: stack, | ||||
| 	} | ||||
| 	l.LogEvent(event) | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // Trace records trace log
 | ||||
| func (l *Logger) Trace(format string, v ...interface{}) { | ||||
| 	l.Log(1, TRACE, format, v...) | ||||
| } | ||||
| 
 | ||||
| // Debug records debug log
 | ||||
| func (l *Logger) Debug(format string, v ...interface{}) { | ||||
| 	l.Log(1, DEBUG, format, v...) | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| // Info records information log
 | ||||
| func (l *Logger) Info(format string, v ...interface{}) { | ||||
| 	l.Log(1, INFO, format, v...) | ||||
| } | ||||
| 
 | ||||
| // Warn records warning log
 | ||||
| func (l *Logger) Warn(format string, v ...interface{}) { | ||||
| 	l.Log(1, WARN, format, v...) | ||||
| } | ||||
| 
 | ||||
| // Error records error log
 | ||||
| func (l *Logger) Error(format string, v ...interface{}) { | ||||
| 	l.Log(1, ERROR, format, v...) | ||||
| } | ||||
| 
 | ||||
| // ErrorWithSkip records error log from "skip" calls back from this function
 | ||||
| func (l *Logger) ErrorWithSkip(skip int, format string, v ...interface{}) { | ||||
| 	l.Log(skip+1, ERROR, format, v...) | ||||
| } | ||||
| 
 | ||||
| // Critical records critical log
 | ||||
| func (l *Logger) Critical(format string, v ...interface{}) { | ||||
| 	l.Log(1, CRITICAL, format, v...) | ||||
| } | ||||
| 
 | ||||
| // CriticalWithSkip records critical log from "skip" calls back from this function
 | ||||
| func (l *Logger) CriticalWithSkip(skip int, format string, v ...interface{}) { | ||||
| 	l.Log(skip+1, CRITICAL, format, v...) | ||||
| } | ||||
| 
 | ||||
| // Fatal records fatal log and exit the process
 | ||||
| func (l *Logger) Fatal(format string, v ...interface{}) { | ||||
| 	l.Log(1, FATAL, format, v...) | ||||
| 	l.Close() | ||||
| 	os.Exit(1) | ||||
| } | ||||
| 
 | ||||
| // FatalWithSkip records fatal log from "skip" calls back from this function and exits the process
 | ||||
| func (l *Logger) FatalWithSkip(skip int, format string, v ...interface{}) { | ||||
| 	l.Log(skip+1, FATAL, format, v...) | ||||
| 	l.Close() | ||||
| 	os.Exit(1) | ||||
| } | ||||
							
								
								
									
										26
									
								
								modules/log/provider.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								modules/log/provider.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,26 @@ | |||
| // 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 | ||||
| 
 | ||||
| // LoggerProvider represents behaviors of a logger provider.
 | ||||
| type LoggerProvider interface { | ||||
| 	Init(config string) error | ||||
| 	EventLogger | ||||
| } | ||||
| 
 | ||||
| type loggerProvider func() LoggerProvider | ||||
| 
 | ||||
| var providers = make(map[string]loggerProvider) | ||||
| 
 | ||||
| // Register registers given logger provider to providers.
 | ||||
| func Register(name string, log loggerProvider) { | ||||
| 	if log == nil { | ||||
| 		panic("log: register provider is nil") | ||||
| 	} | ||||
| 	if _, dup := providers[name]; dup { | ||||
| 		panic("log: register called twice for provider \"" + name + "\"") | ||||
| 	} | ||||
| 	providers[name] = log | ||||
| } | ||||
							
								
								
									
										103
									
								
								modules/log/router.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								modules/log/router.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,103 @@ | |||
| // 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 ( | ||||
| 	"net/http" | ||||
| 	"time" | ||||
| 
 | ||||
| 	macaron "gopkg.in/macaron.v1" | ||||
| ) | ||||
| 
 | ||||
| var statusToColor = map[int][]byte{ | ||||
| 	100: ColorBytes(Bold), | ||||
| 	200: ColorBytes(FgGreen), | ||||
| 	300: ColorBytes(FgYellow), | ||||
| 	304: ColorBytes(FgCyan), | ||||
| 	400: ColorBytes(Bold, FgRed), | ||||
| 	401: ColorBytes(Bold, FgMagenta), | ||||
| 	403: ColorBytes(Bold, FgMagenta), | ||||
| 	500: ColorBytes(Bold, BgRed), | ||||
| } | ||||
| 
 | ||||
| func coloredStatus(status int, s ...string) *ColoredValue { | ||||
| 	color, ok := statusToColor[status] | ||||
| 	if !ok { | ||||
| 		color, ok = statusToColor[(status/100)*100] | ||||
| 	} | ||||
| 	if !ok { | ||||
| 		color = fgBoldBytes | ||||
| 	} | ||||
| 	if len(s) > 0 { | ||||
| 		return NewColoredValueBytes(s[0], &color) | ||||
| 	} | ||||
| 	return NewColoredValueBytes(status, &color) | ||||
| } | ||||
| 
 | ||||
| var methodToColor = map[string][]byte{ | ||||
| 	"GET":    ColorBytes(FgBlue), | ||||
| 	"POST":   ColorBytes(FgGreen), | ||||
| 	"DELETE": ColorBytes(FgRed), | ||||
| 	"PATCH":  ColorBytes(FgCyan), | ||||
| 	"PUT":    ColorBytes(FgYellow, Faint), | ||||
| 	"HEAD":   ColorBytes(FgBlue, Faint), | ||||
| } | ||||
| 
 | ||||
| func coloredMethod(method string) *ColoredValue { | ||||
| 	color, ok := methodToColor[method] | ||||
| 	if !ok { | ||||
| 		return NewColoredValueBytes(method, &fgBoldBytes) | ||||
| 	} | ||||
| 	return NewColoredValueBytes(method, &color) | ||||
| } | ||||
| 
 | ||||
| var durations = []time.Duration{ | ||||
| 	10 * time.Millisecond, | ||||
| 	100 * time.Millisecond, | ||||
| 	1 * time.Second, | ||||
| 	5 * time.Second, | ||||
| 	10 * time.Second, | ||||
| } | ||||
| 
 | ||||
| var durationColors = [][]byte{ | ||||
| 	ColorBytes(FgGreen), | ||||
| 	ColorBytes(Bold), | ||||
| 	ColorBytes(FgYellow), | ||||
| 	ColorBytes(FgRed, Bold), | ||||
| 	ColorBytes(BgRed), | ||||
| } | ||||
| 
 | ||||
| var wayTooLong = ColorBytes(BgMagenta) | ||||
| 
 | ||||
| func coloredTime(duration time.Duration) *ColoredValue { | ||||
| 	for i, k := range durations { | ||||
| 		if duration < k { | ||||
| 			return NewColoredValueBytes(duration, &durationColors[i]) | ||||
| 		} | ||||
| 	} | ||||
| 	return NewColoredValueBytes(duration, &wayTooLong) | ||||
| } | ||||
| 
 | ||||
| // SetupRouterLogger will setup macaron to routing to the main gitea log
 | ||||
| func SetupRouterLogger(m *macaron.Macaron, level Level) { | ||||
| 	if GetLevel() <= level { | ||||
| 		m.Use(RouterHandler(level)) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // RouterHandler is a macaron handler that will log the routing to the default gitea log
 | ||||
| func RouterHandler(level Level) func(ctx *macaron.Context) { | ||||
| 	return func(ctx *macaron.Context) { | ||||
| 		start := time.Now() | ||||
| 
 | ||||
| 		GetLogger("router").Log(0, level, "Started %s %s for %s", coloredMethod(ctx.Req.Method), ctx.Req.RequestURI, ctx.RemoteAddr()) | ||||
| 
 | ||||
| 		rw := ctx.Resp.(macaron.ResponseWriter) | ||||
| 		ctx.Next() | ||||
| 
 | ||||
| 		status := rw.Status() | ||||
| 		GetLogger("router").Log(0, level, "Completed %s %s %v %s in %v", coloredMethod(ctx.Req.Method), ctx.Req.RequestURI, coloredStatus(status), coloredStatus(status, http.StatusText(rw.Status())), coloredTime(time.Since(start))) | ||||
| 	} | ||||
| } | ||||
|  | @ -1,4 +1,5 @@ | |||
| // Copyright 2014 The Gogs Authors. All rights reserved.
 | ||||
| // 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.
 | ||||
| 
 | ||||
|  | @ -6,29 +7,45 @@ package log | |||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"net/smtp" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| 	subjectPhrase = "Diagnostic message from server" | ||||
| ) | ||||
| 
 | ||||
| // SMTPWriter implements LoggerInterface and is used to send emails via given SMTP-server.
 | ||||
| type SMTPWriter struct { | ||||
| 	Username           string   `json:"Username"` | ||||
| 	Password           string   `json:"password"` | ||||
| 	Host               string   `json:"Host"` | ||||
| 	Subject            string   `json:"subject"` | ||||
| 	RecipientAddresses []string `json:"sendTos"` | ||||
| 	Level              int      `json:"level"` | ||||
| type smtpWriter struct { | ||||
| 	owner *SMTPLogger | ||||
| } | ||||
| 
 | ||||
| // NewSMTPWriter creates smtp writer.
 | ||||
| func NewSMTPWriter() LoggerInterface { | ||||
| 	return &SMTPWriter{Level: TRACE} | ||||
| // Write sends the message as an email
 | ||||
| func (s *smtpWriter) Write(p []byte) (int, error) { | ||||
| 	return s.owner.sendMail(p) | ||||
| } | ||||
| 
 | ||||
| // Close does nothing
 | ||||
| func (s *smtpWriter) Close() error { | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // SMTPLogger implements LoggerProvider and is used to send emails via given SMTP-server.
 | ||||
| type SMTPLogger struct { | ||||
| 	BaseLogger | ||||
| 	Username           string   `json:"Username"` | ||||
| 	Password           string   `json:"password"` | ||||
| 	Host               string   `json:"host"` | ||||
| 	Subject            string   `json:"subject"` | ||||
| 	RecipientAddresses []string `json:"sendTos"` | ||||
| 	sendMailFn         func(string, smtp.Auth, string, []string, []byte) error | ||||
| } | ||||
| 
 | ||||
| // NewSMTPLogger creates smtp writer.
 | ||||
| func NewSMTPLogger() LoggerProvider { | ||||
| 	s := &SMTPLogger{} | ||||
| 	s.Level = TRACE | ||||
| 	s.sendMailFn = smtp.SendMail | ||||
| 	return s | ||||
| } | ||||
| 
 | ||||
| // Init smtp writer with json config.
 | ||||
|  | @ -41,49 +58,54 @@ func NewSMTPWriter() LoggerInterface { | |||
| //		"sendTos":["email1","email2"],
 | ||||
| //		"level":LevelError
 | ||||
| //	}
 | ||||
| func (sw *SMTPWriter) Init(jsonconfig string) error { | ||||
| 	return json.Unmarshal([]byte(jsonconfig), sw) | ||||
| func (log *SMTPLogger) Init(jsonconfig string) error { | ||||
| 	err := json.Unmarshal([]byte(jsonconfig), log) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	log.createLogger(&smtpWriter{ | ||||
| 		owner: log, | ||||
| 	}) | ||||
| 	log.sendMailFn = smtp.SendMail | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // WriteMsg writes message in smtp writer.
 | ||||
| // it will send an email with subject and only this message.
 | ||||
| func (sw *SMTPWriter) WriteMsg(msg string, skip, level int) error { | ||||
| 	if level < sw.Level { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	hp := strings.Split(sw.Host, ":") | ||||
| func (log *SMTPLogger) sendMail(p []byte) (int, error) { | ||||
| 	hp := strings.Split(log.Host, ":") | ||||
| 
 | ||||
| 	// Set up authentication information.
 | ||||
| 	auth := smtp.PlainAuth( | ||||
| 		"", | ||||
| 		sw.Username, | ||||
| 		sw.Password, | ||||
| 		log.Username, | ||||
| 		log.Password, | ||||
| 		hp[0], | ||||
| 	) | ||||
| 	// Connect to the server, authenticate, set the sender and recipient,
 | ||||
| 	// and send the email all in one step.
 | ||||
| 	contentType := "Content-Type: text/plain" + "; charset=UTF-8" | ||||
| 	mailmsg := []byte("To: " + strings.Join(sw.RecipientAddresses, ";") + "\r\nFrom: " + sw.Username + "<" + sw.Username + | ||||
| 		">\r\nSubject: " + sw.Subject + "\r\n" + contentType + "\r\n\r\n" + fmt.Sprintf(".%s", time.Now().Format("2006-01-02 15:04:05")) + msg) | ||||
| 
 | ||||
| 	return smtp.SendMail( | ||||
| 		sw.Host, | ||||
| 	mailmsg := []byte("To: " + strings.Join(log.RecipientAddresses, ";") + "\r\nFrom: " + log.Username + "<" + log.Username + | ||||
| 		">\r\nSubject: " + log.Subject + "\r\n" + contentType + "\r\n\r\n") | ||||
| 	mailmsg = append(mailmsg, p...) | ||||
| 	return len(p), log.sendMailFn( | ||||
| 		log.Host, | ||||
| 		auth, | ||||
| 		sw.Username, | ||||
| 		sw.RecipientAddresses, | ||||
| 		log.Username, | ||||
| 		log.RecipientAddresses, | ||||
| 		mailmsg, | ||||
| 	) | ||||
| } | ||||
| 
 | ||||
| // Flush when log should be flushed
 | ||||
| func (sw *SMTPWriter) Flush() { | ||||
| func (log *SMTPLogger) Flush() { | ||||
| } | ||||
| 
 | ||||
| // Destroy when writer is destroy
 | ||||
| func (sw *SMTPWriter) Destroy() { | ||||
| // GetName returns the default name for this implementation
 | ||||
| func (log *SMTPLogger) GetName() string { | ||||
| 	return "smtp" | ||||
| } | ||||
| 
 | ||||
| func init() { | ||||
| 	Register("smtp", NewSMTPWriter) | ||||
| 	Register("smtp", NewSMTPLogger) | ||||
| } | ||||
|  |  | |||
							
								
								
									
										86
									
								
								modules/log/smtp_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								modules/log/smtp_test.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,86 @@ | |||
| // 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 ( | ||||
| 	"fmt" | ||||
| 	"net/smtp" | ||||
| 	"strings" | ||||
| 	"testing" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| ) | ||||
| 
 | ||||
| func TestSMTPLogger(t *testing.T) { | ||||
| 	prefix := "TestPrefix " | ||||
| 	level := INFO | ||||
| 	flags := LstdFlags | LUTC | Lfuncname | ||||
| 	username := "testuser" | ||||
| 	password := "testpassword" | ||||
| 	host := "testhost" | ||||
| 	subject := "testsubject" | ||||
| 	sendTos := []string{"testto1", "testto2"} | ||||
| 
 | ||||
| 	logger := NewSMTPLogger() | ||||
| 	smtpLogger, ok := logger.(*SMTPLogger) | ||||
| 	assert.Equal(t, true, ok) | ||||
| 
 | ||||
| 	err := logger.Init(fmt.Sprintf("{\"prefix\":\"%s\",\"level\":\"%s\",\"flags\":%d,\"username\":\"%s\",\"password\":\"%s\",\"host\":\"%s\",\"subject\":\"%s\",\"sendTos\":[\"%s\",\"%s\"]}", prefix, level.String(), flags, username, password, host, subject, sendTos[0], sendTos[1])) | ||||
| 	assert.NoError(t, err) | ||||
| 
 | ||||
| 	assert.Equal(t, flags, smtpLogger.Flags) | ||||
| 	assert.Equal(t, level, smtpLogger.Level) | ||||
| 	assert.Equal(t, level, logger.GetLevel()) | ||||
| 
 | ||||
| 	location, _ := time.LoadLocation("EST") | ||||
| 
 | ||||
| 	date := time.Date(2019, time.January, 13, 22, 3, 30, 15, location) | ||||
| 
 | ||||
| 	dateString := date.UTC().Format("2006/01/02 15:04:05") | ||||
| 
 | ||||
| 	event := Event{ | ||||
| 		level:    INFO, | ||||
| 		msg:      "TEST MSG", | ||||
| 		caller:   "CALLER", | ||||
| 		filename: "FULL/FILENAME", | ||||
| 		line:     1, | ||||
| 		time:     date, | ||||
| 	} | ||||
| 
 | ||||
| 	expected := fmt.Sprintf("%s%s %s:%d:%s [%c] %s\n", prefix, dateString, event.filename, event.line, event.caller, strings.ToUpper(event.level.String())[0], event.msg) | ||||
| 
 | ||||
| 	var envToHost string | ||||
| 	var envFrom string | ||||
| 	var envTo []string | ||||
| 	var envMsg []byte | ||||
| 	smtpLogger.sendMailFn = func(addr string, a smtp.Auth, from string, to []string, msg []byte) error { | ||||
| 		envToHost = addr | ||||
| 		envFrom = from | ||||
| 		envTo = to | ||||
| 		envMsg = msg | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	err = logger.LogEvent(&event) | ||||
| 	assert.NoError(t, err) | ||||
| 	assert.Equal(t, host, envToHost) | ||||
| 	assert.Equal(t, username, envFrom) | ||||
| 	assert.Equal(t, sendTos, envTo) | ||||
| 	assert.Contains(t, string(envMsg), expected) | ||||
| 
 | ||||
| 	logger.Flush() | ||||
| 
 | ||||
| 	event.level = WARN | ||||
| 	expected = fmt.Sprintf("%s%s %s:%d:%s [%c] %s\n", prefix, dateString, event.filename, event.line, event.caller, strings.ToUpper(event.level.String())[0], event.msg) | ||||
| 	err = logger.LogEvent(&event) | ||||
| 	assert.NoError(t, err) | ||||
| 	assert.Equal(t, host, envToHost) | ||||
| 	assert.Equal(t, username, envFrom) | ||||
| 	assert.Equal(t, sendTos, envTo) | ||||
| 	assert.Contains(t, string(envMsg), expected) | ||||
| 
 | ||||
| 	logger.Close() | ||||
| } | ||||
							
								
								
									
										83
									
								
								modules/log/stack.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								modules/log/stack.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,83 @@ | |||
| // 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/ioutil" | ||||
| 	"runtime" | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| 	unknown = []byte("???") | ||||
| ) | ||||
| 
 | ||||
| // Stack will skip back the provided number of frames and return a stack trace with source code.
 | ||||
| // Although we could just use debug.Stack(), this routine will return the source code and
 | ||||
| // skip back the provided number of frames - i.e. allowing us to ignore preceding function calls.
 | ||||
| // A skip of 0 returns the stack trace for the calling function, not including this call.
 | ||||
| // If the problem is a lack of memory of course all this is not going to work...
 | ||||
| func Stack(skip int) string { | ||||
| 	buf := new(bytes.Buffer) | ||||
| 
 | ||||
| 	// Store the last file we opened as its probable that the preceding stack frame
 | ||||
| 	// will be in the same file
 | ||||
| 	var lines [][]byte | ||||
| 	var lastFilename string | ||||
| 	for i := skip + 1; ; i++ { // Skip over frames
 | ||||
| 		programCounter, filename, lineNumber, ok := runtime.Caller(i) | ||||
| 		// If we can't retrieve the information break - basically we're into go internals at this point.
 | ||||
| 		if !ok { | ||||
| 			break | ||||
| 		} | ||||
| 
 | ||||
| 		// Print equivalent of debug.Stack()
 | ||||
| 		fmt.Fprintf(buf, "%s:%d (0x%x)\n", filename, lineNumber, programCounter) | ||||
| 		// Now try to print the offending line
 | ||||
| 		if filename != lastFilename { | ||||
| 			data, err := ioutil.ReadFile(filename) | ||||
| 			if err != nil { | ||||
| 				// can't read this sourcefile
 | ||||
| 				// likely we don't have the sourcecode available
 | ||||
| 				continue | ||||
| 			} | ||||
| 			lines = bytes.Split(data, []byte{'\n'}) | ||||
| 			lastFilename = filename | ||||
| 		} | ||||
| 		fmt.Fprintf(buf, "\t%s: %s\n", functionName(programCounter), source(lines, lineNumber)) | ||||
| 	} | ||||
| 	return buf.String() | ||||
| } | ||||
| 
 | ||||
| // functionName converts the provided programCounter into a function name
 | ||||
| func functionName(programCounter uintptr) []byte { | ||||
| 	function := runtime.FuncForPC(programCounter) | ||||
| 	if function == nil { | ||||
| 		return unknown | ||||
| 	} | ||||
| 	name := []byte(function.Name()) | ||||
| 
 | ||||
| 	// Because we provide the filename we can drop the preceding package name.
 | ||||
| 	if lastslash := bytes.LastIndex(name, []byte("/")); lastslash >= 0 { | ||||
| 		name = name[lastslash+1:] | ||||
| 	} | ||||
| 	// And the current package name.
 | ||||
| 	if period := bytes.Index(name, []byte(".")); period >= 0 { | ||||
| 		name = name[period+1:] | ||||
| 	} | ||||
| 	// And we should just replace the interpunct with a dot
 | ||||
| 	name = bytes.Replace(name, []byte("·"), []byte("."), -1) | ||||
| 	return name | ||||
| } | ||||
| 
 | ||||
| // source returns a space-trimmed slice of the n'th line.
 | ||||
| func source(lines [][]byte, n int) []byte { | ||||
| 	n-- // in stack trace, lines are 1-indexed but our array is 0-indexed
 | ||||
| 	if n < 0 || n >= len(lines) { | ||||
| 		return unknown | ||||
| 	} | ||||
| 	return bytes.TrimSpace(lines[n]) | ||||
| } | ||||
|  | @ -12,7 +12,6 @@ import ( | |||
| 
 | ||||
| // XORMLogBridge a logger bridge from Logger to xorm
 | ||||
| type XORMLogBridge struct { | ||||
| 	loggers []*Logger | ||||
| 	showSQL bool | ||||
| 	level   core.LogLevel | ||||
| } | ||||
|  | @ -22,110 +21,80 @@ var ( | |||
| 	XORMLogger *XORMLogBridge | ||||
| ) | ||||
| 
 | ||||
| // DiscardXORMLogger inits a blank logger for xorm
 | ||||
| func DiscardXORMLogger() { | ||||
| // InitXORMLogger inits a log bridge for xorm
 | ||||
| func InitXORMLogger(showSQL bool) { | ||||
| 	XORMLogger = &XORMLogBridge{ | ||||
| 		showSQL: false, | ||||
| 		showSQL: showSQL, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // NewXORMLogger generate logger for xorm FIXME: configable
 | ||||
| func NewXORMLogger(bufferlen int64, mode, config string) { | ||||
| 	logger := newLogger(bufferlen) | ||||
| 	logger.SetLogger(mode, config) | ||||
| 	if XORMLogger == nil { | ||||
| 		XORMLogger = &XORMLogBridge{ | ||||
| 			showSQL: true, | ||||
| 		} | ||||
| 	} | ||||
| 	XORMLogger.loggers = append(XORMLogger.loggers, logger) | ||||
| // GetGiteaLevel returns the minimum Gitea logger level
 | ||||
| func (l *XORMLogBridge) GetGiteaLevel() Level { | ||||
| 	return GetLogger("xorm").GetLevel() | ||||
| } | ||||
| 
 | ||||
| func (l *XORMLogBridge) writerMsg(skip, level int, msg string) error { | ||||
| 	for _, logger := range l.loggers { | ||||
| 		if err := logger.writerMsg(skip, level, msg); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| // Log a message with defined skip and at logging level
 | ||||
| func (l *XORMLogBridge) Log(skip int, level Level, format string, v ...interface{}) error { | ||||
| 	return GetLogger("xorm").Log(skip+1, level, format, v...) | ||||
| } | ||||
| 
 | ||||
| // Debug show debug log
 | ||||
| func (l *XORMLogBridge) Debug(v ...interface{}) { | ||||
| 	if l.level <= core.LOG_DEBUG { | ||||
| 		msg := fmt.Sprint(v...) | ||||
| 		l.writerMsg(0, DEBUG, "[D]"+msg) | ||||
| 	} | ||||
| 	l.Log(2, DEBUG, fmt.Sprint(v...)) | ||||
| } | ||||
| 
 | ||||
| // Debugf show debug log
 | ||||
| func (l *XORMLogBridge) Debugf(format string, v ...interface{}) { | ||||
| 	if l.level <= core.LOG_DEBUG { | ||||
| 		for _, logger := range l.loggers { | ||||
| 			logger.Debug(format, v...) | ||||
| 		} | ||||
| 	} | ||||
| 	l.Log(2, DEBUG, format, v...) | ||||
| } | ||||
| 
 | ||||
| // Error show error log
 | ||||
| func (l *XORMLogBridge) Error(v ...interface{}) { | ||||
| 	if l.level <= core.LOG_ERR { | ||||
| 		msg := fmt.Sprint(v...) | ||||
| 		l.writerMsg(0, ERROR, "[E]"+msg) | ||||
| 	} | ||||
| 	l.Log(2, ERROR, fmt.Sprint(v...)) | ||||
| } | ||||
| 
 | ||||
| // Errorf show error log
 | ||||
| func (l *XORMLogBridge) Errorf(format string, v ...interface{}) { | ||||
| 	if l.level <= core.LOG_ERR { | ||||
| 		for _, logger := range l.loggers { | ||||
| 			logger.Error(0, format, v...) | ||||
| 		} | ||||
| 	} | ||||
| 	l.Log(2, ERROR, format, v...) | ||||
| } | ||||
| 
 | ||||
| // Info show information level log
 | ||||
| func (l *XORMLogBridge) Info(v ...interface{}) { | ||||
| 	if l.level <= core.LOG_INFO { | ||||
| 		msg := fmt.Sprint(v...) | ||||
| 		l.writerMsg(0, INFO, "[I]"+msg) | ||||
| 	} | ||||
| 	l.Log(2, INFO, fmt.Sprint(v...)) | ||||
| } | ||||
| 
 | ||||
| // Infof show information level log
 | ||||
| func (l *XORMLogBridge) Infof(format string, v ...interface{}) { | ||||
| 	if l.level <= core.LOG_INFO { | ||||
| 		for _, logger := range l.loggers { | ||||
| 			logger.Info(format, v...) | ||||
| 		} | ||||
| 	} | ||||
| 	l.Log(2, INFO, format, v...) | ||||
| } | ||||
| 
 | ||||
| // Warn show warning log
 | ||||
| func (l *XORMLogBridge) Warn(v ...interface{}) { | ||||
| 	if l.level <= core.LOG_WARNING { | ||||
| 		msg := fmt.Sprint(v...) | ||||
| 		l.writerMsg(0, WARN, "[W] "+msg) | ||||
| 	} | ||||
| 	l.Log(2, WARN, fmt.Sprint(v...)) | ||||
| } | ||||
| 
 | ||||
| // Warnf show warnning log
 | ||||
| func (l *XORMLogBridge) Warnf(format string, v ...interface{}) { | ||||
| 	if l.level <= core.LOG_WARNING { | ||||
| 		for _, logger := range l.loggers { | ||||
| 			logger.Warn(format, v...) | ||||
| 		} | ||||
| 	} | ||||
| 	l.Log(2, WARN, format, v...) | ||||
| } | ||||
| 
 | ||||
| // Level get logger level
 | ||||
| func (l *XORMLogBridge) Level() core.LogLevel { | ||||
| 	return l.level | ||||
| 	switch l.GetGiteaLevel() { | ||||
| 	case TRACE, DEBUG: | ||||
| 		return core.LOG_DEBUG | ||||
| 	case INFO: | ||||
| 		return core.LOG_INFO | ||||
| 	case WARN: | ||||
| 		return core.LOG_WARNING | ||||
| 	case ERROR, CRITICAL: | ||||
| 		return core.LOG_ERR | ||||
| 	} | ||||
| 	return core.LOG_OFF | ||||
| } | ||||
| 
 | ||||
| // SetLevel set logger level
 | ||||
| func (l *XORMLogBridge) SetLevel(level core.LogLevel) { | ||||
| 	l.level = level | ||||
| // SetLevel set the logger level
 | ||||
| func (l *XORMLogBridge) SetLevel(lvl core.LogLevel) { | ||||
| } | ||||
| 
 | ||||
| // ShowSQL set if record SQL
 | ||||
|  |  | |||
|  | @ -258,7 +258,7 @@ func processMailQueue() { | |||
| 		case msg := <-mailQueue: | ||||
| 			log.Trace("New e-mail sending request %s: %s", msg.GetHeader("To"), msg.Info) | ||||
| 			if err := gomail.Send(Sender, msg.Message); err != nil { | ||||
| 				log.Error(3, "Failed to send emails %s: %s - %v", msg.GetHeader("To"), msg.Info, err) | ||||
| 				log.Error("Failed to send emails %s: %s - %v", msg.GetHeader("To"), msg.Info, err) | ||||
| 			} else { | ||||
| 				log.Trace("E-mails sent %s: %s", msg.GetHeader("To"), msg.Info) | ||||
| 			} | ||||
|  |  | |||
							
								
								
									
										8
									
								
								modules/markup/external/external.go
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								modules/markup/external/external.go
									
									
									
									
										vendored
									
									
								
							|  | @ -67,7 +67,7 @@ func (p *Parser) Render(rawBytes []byte, urlPrefix string, metas map[string]stri | |||
| 		// write to temp file
 | ||||
| 		f, err := ioutil.TempFile("", "gitea_input") | ||||
| 		if err != nil { | ||||
| 			log.Error(4, "%s create temp file when rendering %s failed: %v", p.Name(), p.Command, err) | ||||
| 			log.Error("%s create temp file when rendering %s failed: %v", p.Name(), p.Command, err) | ||||
| 			return []byte("") | ||||
| 		} | ||||
| 		defer os.Remove(f.Name()) | ||||
|  | @ -75,13 +75,13 @@ func (p *Parser) Render(rawBytes []byte, urlPrefix string, metas map[string]stri | |||
| 		_, err = io.Copy(f, rd) | ||||
| 		if err != nil { | ||||
| 			f.Close() | ||||
| 			log.Error(4, "%s write data to temp file when rendering %s failed: %v", p.Name(), p.Command, err) | ||||
| 			log.Error("%s write data to temp file when rendering %s failed: %v", p.Name(), p.Command, err) | ||||
| 			return []byte("") | ||||
| 		} | ||||
| 
 | ||||
| 		err = f.Close() | ||||
| 		if err != nil { | ||||
| 			log.Error(4, "%s close temp file when rendering %s failed: %v", p.Name(), p.Command, err) | ||||
| 			log.Error("%s close temp file when rendering %s failed: %v", p.Name(), p.Command, err) | ||||
| 			return []byte("") | ||||
| 		} | ||||
| 		args = append(args, f.Name()) | ||||
|  | @ -98,7 +98,7 @@ func (p *Parser) Render(rawBytes []byte, urlPrefix string, metas map[string]stri | |||
| 	} | ||||
| 	cmd.Stdout = buf | ||||
| 	if err := cmd.Run(); err != nil { | ||||
| 		log.Error(4, "%s render run command %s %v failed: %v", p.Name(), commands[0], args, err) | ||||
| 		log.Error("%s render run command %s %v failed: %v", p.Name(), commands[0], args, err) | ||||
| 		return []byte("") | ||||
| 	} | ||||
| 	return buf.Bytes() | ||||
|  |  | |||
|  | @ -74,7 +74,7 @@ func render(parser Parser, rawBytes []byte, urlPrefix string, metas map[string]s | |||
| 	// TODO: one day the error should be returned.
 | ||||
| 	result, err := PostProcess(result, urlPrefix, metas, isWiki) | ||||
| 	if err != nil { | ||||
| 		log.Error(3, "PostProcess: %v", err) | ||||
| 		log.Error("PostProcess: %v", err) | ||||
| 	} | ||||
| 	return SanitizeBytes(result) | ||||
| } | ||||
|  |  | |||
|  | @ -35,7 +35,7 @@ func (Parser) Extensions() []string { | |||
| func Render(rawBytes []byte, urlPrefix string, metas map[string]string, isWiki bool) (result []byte) { | ||||
| 	defer func() { | ||||
| 		if err := recover(); err != nil { | ||||
| 			log.Error(4, "Panic in orgmode.Render: %v Just returning the rawBytes", err) | ||||
| 			log.Error("Panic in orgmode.Render: %v Just returning the rawBytes", err) | ||||
| 			result = rawBytes | ||||
| 		} | ||||
| 	}() | ||||
|  |  | |||
|  | @ -29,7 +29,7 @@ func (r *indexerNotifier) NotifyCreateIssueComment(doer *models.User, repo *mode | |||
| 	if comment.Type == models.CommentTypeComment { | ||||
| 		if issue.Comments == nil { | ||||
| 			if err := issue.LoadDiscussComments(); err != nil { | ||||
| 				log.Error(4, "LoadComments failed: %v", err) | ||||
| 				log.Error("LoadComments failed: %v", err) | ||||
| 				return | ||||
| 			} | ||||
| 		} else { | ||||
|  | @ -63,7 +63,7 @@ func (r *indexerNotifier) NotifyUpdateComment(doer *models.User, c *models.Comme | |||
| 
 | ||||
| 		if !found { | ||||
| 			if err := c.Issue.LoadDiscussComments(); err != nil { | ||||
| 				log.Error(4, "LoadComments failed: %v", err) | ||||
| 				log.Error("LoadComments failed: %v", err) | ||||
| 				return | ||||
| 			} | ||||
| 		} | ||||
|  | @ -87,7 +87,7 @@ func (r *indexerNotifier) NotifyDeleteComment(doer *models.User, comment *models | |||
| 
 | ||||
| 		if !found { | ||||
| 			if err := comment.Issue.LoadDiscussComments(); err != nil { | ||||
| 				log.Error(4, "LoadComments failed: %v", err) | ||||
| 				log.Error("LoadComments failed: %v", err) | ||||
| 				return | ||||
| 			} | ||||
| 		} | ||||
|  |  | |||
|  | @ -37,25 +37,25 @@ func (m *mailNotifier) NotifyCreateIssueComment(doer *models.User, repo *models. | |||
| 	} | ||||
| 
 | ||||
| 	if err := comment.MailParticipants(act, issue); err != nil { | ||||
| 		log.Error(4, "MailParticipants: %v", err) | ||||
| 		log.Error("MailParticipants: %v", err) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (m *mailNotifier) NotifyNewIssue(issue *models.Issue) { | ||||
| 	if err := issue.MailParticipants(); err != nil { | ||||
| 		log.Error(4, "MailParticipants: %v", err) | ||||
| 		log.Error("MailParticipants: %v", err) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (m *mailNotifier) NotifyIssueChangeStatus(doer *models.User, issue *models.Issue, isClosed bool) { | ||||
| 	if err := issue.MailParticipants(); err != nil { | ||||
| 		log.Error(4, "MailParticipants: %v", err) | ||||
| 		log.Error("MailParticipants: %v", err) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (m *mailNotifier) NotifyNewPullRequest(pr *models.PullRequest) { | ||||
| 	if err := pr.Issue.MailParticipants(); err != nil { | ||||
| 		log.Error(4, "MailParticipants: %v", err) | ||||
| 		log.Error("MailParticipants: %v", err) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | @ -69,6 +69,6 @@ func (m *mailNotifier) NotifyPullRequestReview(pr *models.PullRequest, r *models | |||
| 		act = models.ActionCommentIssue | ||||
| 	} | ||||
| 	if err := comment.MailParticipants(act, pr.Issue); err != nil { | ||||
| 		log.Error(4, "MailParticipants: %v", err) | ||||
| 		log.Error("MailParticipants: %v", err) | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -39,7 +39,7 @@ func (ns *notificationService) Run() { | |||
| 		select { | ||||
| 		case opts := <-ns.issueQueue: | ||||
| 			if err := models.CreateOrUpdateIssueNotifications(opts.issue, opts.notificationAuthorID); err != nil { | ||||
| 				log.Error(4, "Was unable to create issue notification: %v", err) | ||||
| 				log.Error("Was unable to create issue notification: %v", err) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  |  | |||
|  | @ -17,12 +17,12 @@ import ( | |||
| func DumpMemProfileForUsername(pprofDataPath, username string) { | ||||
| 	f, err := ioutil.TempFile(pprofDataPath, fmt.Sprintf("memprofile_%s_", username)) | ||||
| 	if err != nil { | ||||
| 		log.GitLogger.Fatal(4, "Could not create memory profile: %v", err) | ||||
| 		log.GitLogger.Fatal("Could not create memory profile: %v", err) | ||||
| 	} | ||||
| 	defer f.Close() | ||||
| 	runtime.GC() // get up-to-date statistics
 | ||||
| 	if err := pprof.WriteHeapProfile(f); err != nil { | ||||
| 		log.GitLogger.Fatal(4, "Could not write memory profile: %v", err) | ||||
| 		log.GitLogger.Fatal("Could not write memory profile: %v", err) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | @ -31,7 +31,7 @@ func DumpMemProfileForUsername(pprofDataPath, username string) { | |||
| func DumpCPUProfileForUsername(pprofDataPath, username string) func() { | ||||
| 	f, err := ioutil.TempFile(pprofDataPath, fmt.Sprintf("cpuprofile_%s_", username)) | ||||
| 	if err != nil { | ||||
| 		log.GitLogger.Fatal(4, "Could not create cpu profile: %v", err) | ||||
| 		log.GitLogger.Fatal("Could not create cpu profile: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	pprof.StartCPUProfile(f) | ||||
|  |  | |||
|  | @ -35,7 +35,7 @@ func newCacheService() { | |||
| 	case "redis", "memcache": | ||||
| 		CacheService.Conn = strings.Trim(sec.Key("HOST").String(), "\" ") | ||||
| 	default: | ||||
| 		log.Fatal(4, "Unknown cache adapter: %s", CacheService.Adapter) | ||||
| 		log.Fatal("Unknown cache adapter: %s", CacheService.Adapter) | ||||
| 	} | ||||
| 	CacheService.TTL = sec.Key("ITEM_TTL").MustDuration(16 * time.Hour) | ||||
| 
 | ||||
|  |  | |||
|  | @ -119,6 +119,6 @@ var ( | |||
| 
 | ||||
| func newCron() { | ||||
| 	if err := Cfg.Section("cron").MapTo(&Cron); err != nil { | ||||
| 		log.Fatal(4, "Failed to map Cron settings: %v", err) | ||||
| 		log.Fatal("Failed to map Cron settings: %v", err) | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -56,13 +56,13 @@ var ( | |||
| 
 | ||||
| func newGit() { | ||||
| 	if err := Cfg.Section("git").MapTo(&Git); err != nil { | ||||
| 		log.Fatal(4, "Failed to map Git settings: %v", err) | ||||
| 		log.Fatal("Failed to map Git settings: %v", err) | ||||
| 	} | ||||
| 	git.DefaultCommandExecutionTimeout = time.Duration(Git.Timeout.Default) * time.Second | ||||
| 
 | ||||
| 	binVersion, err := git.BinVersion() | ||||
| 	if err != nil { | ||||
| 		log.Fatal(4, "Error retrieving git version: %v", err) | ||||
| 		log.Fatal("Error retrieving git version: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	if version.Compare(binVersion, "2.9", ">=") { | ||||
|  |  | |||
|  | @ -5,40 +5,238 @@ | |||
| package setting | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"encoding/json" | ||||
| 	golog "log" | ||||
| 	"os" | ||||
| 	"path" | ||||
| 	"path/filepath" | ||||
| 	"runtime" | ||||
| 	"strings" | ||||
| 
 | ||||
| 	"code.gitea.io/gitea/modules/log" | ||||
| 	"github.com/go-xorm/core" | ||||
| 
 | ||||
| 	ini "gopkg.in/ini.v1" | ||||
| ) | ||||
| 
 | ||||
| var logLevels = map[string]string{ | ||||
| 	"Trace":    "0", | ||||
| 	"Debug":    "1", | ||||
| 	"Info":     "2", | ||||
| 	"Warn":     "3", | ||||
| 	"Error":    "4", | ||||
| 	"Critical": "5", | ||||
| type defaultLogOptions struct { | ||||
| 	levelName      string // LogLevel
 | ||||
| 	flags          string | ||||
| 	filename       string //path.Join(LogRootPath, "gitea.log")
 | ||||
| 	bufferLength   int64 | ||||
| 	disableConsole bool | ||||
| } | ||||
| 
 | ||||
| func getLogLevel(section string, key string, defaultValue string) string { | ||||
| 	validLevels := []string{"Trace", "Debug", "Info", "Warn", "Error", "Critical"} | ||||
| 	return Cfg.Section(section).Key(key).In(defaultValue, validLevels) | ||||
| func newDefaultLogOptions() defaultLogOptions { | ||||
| 	return defaultLogOptions{ | ||||
| 		levelName:      LogLevel, | ||||
| 		flags:          "stdflags", | ||||
| 		filename:       filepath.Join(LogRootPath, "gitea.log"), | ||||
| 		bufferLength:   10000, | ||||
| 		disableConsole: false, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // SubLogDescription describes a sublogger
 | ||||
| type SubLogDescription struct { | ||||
| 	Name     string | ||||
| 	Provider string | ||||
| 	Config   string | ||||
| } | ||||
| 
 | ||||
| // LogDescription describes a named logger
 | ||||
| type LogDescription struct { | ||||
| 	Name               string | ||||
| 	SubLogDescriptions []SubLogDescription | ||||
| } | ||||
| 
 | ||||
| func getLogLevel(section *ini.Section, key string, defaultValue string) string { | ||||
| 	value := section.Key(key).MustString("info") | ||||
| 	return log.FromString(value).String() | ||||
| } | ||||
| 
 | ||||
| func getStacktraceLogLevel(section *ini.Section, key string, defaultValue string) string { | ||||
| 	value := section.Key(key).MustString("none") | ||||
| 	return log.FromString(value).String() | ||||
| } | ||||
| 
 | ||||
| func generateLogConfig(sec *ini.Section, name string, defaults defaultLogOptions) (mode, jsonConfig, levelName string) { | ||||
| 	levelName = getLogLevel(sec, "LEVEL", LogLevel) | ||||
| 	level := log.FromString(levelName) | ||||
| 	stacktraceLevelName := getStacktraceLogLevel(sec, "STACKTRACE_LEVEL", StacktraceLogLevel) | ||||
| 	stacktraceLevel := log.FromString(stacktraceLevelName) | ||||
| 	mode = name | ||||
| 	keys := sec.Keys() | ||||
| 	logPath := defaults.filename | ||||
| 	flags := log.FlagsFromString(defaults.flags) | ||||
| 	expression := "" | ||||
| 	prefix := "" | ||||
| 	for _, key := range keys { | ||||
| 		switch key.Name() { | ||||
| 		case "MODE": | ||||
| 			mode = key.MustString(name) | ||||
| 		case "FILE_NAME": | ||||
| 			logPath = key.MustString(defaults.filename) | ||||
| 			forcePathSeparator(logPath) | ||||
| 			if !filepath.IsAbs(logPath) { | ||||
| 				logPath = path.Join(LogRootPath, logPath) | ||||
| 			} | ||||
| 		case "FLAGS": | ||||
| 			flags = log.FlagsFromString(key.MustString(defaults.flags)) | ||||
| 		case "EXPRESSION": | ||||
| 			expression = key.MustString("") | ||||
| 		case "PREFIX": | ||||
| 			prefix = key.MustString("") | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	logConfig := map[string]interface{}{ | ||||
| 		"level":           level.String(), | ||||
| 		"expression":      expression, | ||||
| 		"prefix":          prefix, | ||||
| 		"flags":           flags, | ||||
| 		"stacktraceLevel": stacktraceLevel.String(), | ||||
| 	} | ||||
| 
 | ||||
| 	// Generate log configuration.
 | ||||
| 	switch mode { | ||||
| 	case "console": | ||||
| 		useStderr := sec.Key("STDERR").MustBool(false) | ||||
| 		logConfig["stderr"] = useStderr | ||||
| 		if useStderr { | ||||
| 			logConfig["colorize"] = sec.Key("COLORIZE").MustBool(log.CanColorStderr) | ||||
| 		} else { | ||||
| 			logConfig["colorize"] = sec.Key("COLORIZE").MustBool(log.CanColorStdout) | ||||
| 		} | ||||
| 
 | ||||
| 	case "file": | ||||
| 		if err := os.MkdirAll(path.Dir(logPath), os.ModePerm); err != nil { | ||||
| 			panic(err.Error()) | ||||
| 		} | ||||
| 
 | ||||
| 		logConfig["colorize"] = sec.Key("COLORIZE").MustBool(runtime.GOOS != "windows") | ||||
| 		logConfig["filename"] = logPath | ||||
| 		logConfig["rotate"] = sec.Key("LOG_ROTATE").MustBool(true) | ||||
| 		logConfig["maxsize"] = 1 << uint(sec.Key("MAX_SIZE_SHIFT").MustInt(28)) | ||||
| 		logConfig["daily"] = sec.Key("DAILY_ROTATE").MustBool(true) | ||||
| 		logConfig["maxdays"] = sec.Key("MAX_DAYS").MustInt(7) | ||||
| 		logConfig["compress"] = sec.Key("COMPRESS").MustBool(true) | ||||
| 		logConfig["compressionLevel"] = sec.Key("COMPRESSION_LEVEL").MustInt(-1) | ||||
| 	case "conn": | ||||
| 		logConfig["reconnectOnMsg"] = sec.Key("RECONNECT_ON_MSG").MustBool() | ||||
| 		logConfig["reconnect"] = sec.Key("RECONNECT").MustBool() | ||||
| 		logConfig["net"] = sec.Key("PROTOCOL").In("tcp", []string{"tcp", "unix", "udp"}) | ||||
| 		logConfig["addr"] = sec.Key("ADDR").MustString(":7020") | ||||
| 	case "smtp": | ||||
| 		logConfig["username"] = sec.Key("USER").MustString("example@example.com") | ||||
| 		logConfig["password"] = sec.Key("PASSWD").MustString("******") | ||||
| 		logConfig["host"] = sec.Key("HOST").MustString("127.0.0.1:25") | ||||
| 		logConfig["sendTos"] = sec.Key("RECEIVERS").MustString("[]") | ||||
| 		logConfig["subject"] = sec.Key("SUBJECT").MustString("Diagnostic message from Gitea") | ||||
| 	} | ||||
| 
 | ||||
| 	logConfig["colorize"] = sec.Key("COLORIZE").MustBool(false) | ||||
| 
 | ||||
| 	byteConfig, err := json.Marshal(logConfig) | ||||
| 	if err != nil { | ||||
| 		log.Error("Failed to marshal log configuration: %v %v", logConfig, err) | ||||
| 		return | ||||
| 	} | ||||
| 	jsonConfig = string(byteConfig) | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| func generateNamedLogger(key string, options defaultLogOptions) *LogDescription { | ||||
| 	description := LogDescription{ | ||||
| 		Name: key, | ||||
| 	} | ||||
| 
 | ||||
| 	sections := strings.Split(Cfg.Section("log").Key(strings.ToUpper(key)).MustString(""), ",") | ||||
| 
 | ||||
| 	//description.Configs = make([]string, len(description.Sections))
 | ||||
| 
 | ||||
| 	for i := 0; i < len(sections); i++ { | ||||
| 		sections[i] = strings.TrimSpace(sections[i]) | ||||
| 	} | ||||
| 
 | ||||
| 	for _, name := range sections { | ||||
| 		if len(name) == 0 || (name == "console" && options.disableConsole) { | ||||
| 			continue | ||||
| 		} | ||||
| 		sec, err := Cfg.GetSection("log." + name + "." + key) | ||||
| 		if err != nil { | ||||
| 			sec, _ = Cfg.NewSection("log." + name + "." + key) | ||||
| 		} | ||||
| 
 | ||||
| 		provider, config, levelName := generateLogConfig(sec, name, options) | ||||
| 
 | ||||
| 		log.NewNamedLogger(key, options.bufferLength, name, provider, config) | ||||
| 
 | ||||
| 		description.SubLogDescriptions = append(description.SubLogDescriptions, SubLogDescription{ | ||||
| 			Name:     name, | ||||
| 			Provider: provider, | ||||
| 			Config:   config, | ||||
| 		}) | ||||
| 		log.Info("%s Log: %s(%s:%s)", strings.Title(key), strings.Title(name), provider, levelName) | ||||
| 	} | ||||
| 
 | ||||
| 	LogDescriptions[key] = &description | ||||
| 
 | ||||
| 	return &description | ||||
| } | ||||
| 
 | ||||
| func newMacaronLogService() { | ||||
| 	options := newDefaultLogOptions() | ||||
| 	options.filename = filepath.Join(LogRootPath, "macaron.log") | ||||
| 	options.bufferLength = Cfg.Section("log").Key("BUFFER_LEN").MustInt64(10000) | ||||
| 
 | ||||
| 	Cfg.Section("log").Key("MACARON").MustString("file") | ||||
| 	if RedirectMacaronLog { | ||||
| 		generateNamedLogger("macaron", options) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func newAccessLogService() { | ||||
| 	EnableAccessLog = Cfg.Section("log").Key("ENABLE_ACCESS_LOG").MustBool(false) | ||||
| 	AccessLogTemplate = Cfg.Section("log").Key("ACCESS_LOG_TEMPLATE").MustString( | ||||
| 		`{{.Ctx.RemoteAddr}} - {{.Identity}} {{.Start.Format "[02/Jan/2006:15:04:05 -0700]" }} "{{.Ctx.Req.Method}} {{.Ctx.Req.RequestURI}} {{.Ctx.Req.Proto}}" {{.ResponseWriter.Status}} {{.ResponseWriter.Size}} "{{.Ctx.Req.Referer}}\" \"{{.Ctx.Req.UserAgent}}"`) | ||||
| 	Cfg.Section("log").Key("ACCESS").MustString("file") | ||||
| 	if EnableAccessLog { | ||||
| 		options := newDefaultLogOptions() | ||||
| 		options.filename = filepath.Join(LogRootPath, "access.log") | ||||
| 		options.flags = "" // For the router we don't want any prefixed flags
 | ||||
| 		options.bufferLength = Cfg.Section("log").Key("BUFFER_LEN").MustInt64(10000) | ||||
| 		generateNamedLogger("access", options) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func newRouterLogService() { | ||||
| 	Cfg.Section("log").Key("ROUTER").MustString("console") | ||||
| 
 | ||||
| 	if !DisableRouterLog && RedirectMacaronLog { | ||||
| 		options := newDefaultLogOptions() | ||||
| 		options.filename = filepath.Join(LogRootPath, "router.log") | ||||
| 		options.flags = "date,time" // For the router we don't want any prefixed flags
 | ||||
| 		options.bufferLength = Cfg.Section("log").Key("BUFFER_LEN").MustInt64(10000) | ||||
| 		generateNamedLogger("router", options) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func newLogService() { | ||||
| 	log.Info("Gitea v%s%s", AppVer, AppBuiltWith) | ||||
| 
 | ||||
| 	LogModes = strings.Split(Cfg.Section("log").Key("MODE").MustString("console"), ",") | ||||
| 	LogConfigs = make([]string, len(LogModes)) | ||||
| 	options := newDefaultLogOptions() | ||||
| 	options.bufferLength = Cfg.Section("log").Key("BUFFER_LEN").MustInt64(10000) | ||||
| 
 | ||||
| 	description := LogDescription{ | ||||
| 		Name: log.DEFAULT, | ||||
| 	} | ||||
| 
 | ||||
| 	sections := strings.Split(Cfg.Section("log").Key("MODE").MustString("console"), ",") | ||||
| 
 | ||||
| 	useConsole := false | ||||
| 	for i := 0; i < len(LogModes); i++ { | ||||
| 		LogModes[i] = strings.TrimSpace(LogModes[i]) | ||||
| 		if LogModes[i] == "console" { | ||||
| 	for i := 0; i < len(sections); i++ { | ||||
| 		sections[i] = strings.TrimSpace(sections[i]) | ||||
| 		if sections[i] == "console" { | ||||
| 			useConsole = true | ||||
| 		} | ||||
| 	} | ||||
|  | @ -47,140 +245,47 @@ func newLogService() { | |||
| 		log.DelLogger("console") | ||||
| 	} | ||||
| 
 | ||||
| 	for i, mode := range LogModes { | ||||
| 		sec, err := Cfg.GetSection("log." + mode) | ||||
| 	for _, name := range sections { | ||||
| 		if len(name) == 0 { | ||||
| 			continue | ||||
| 		} | ||||
| 
 | ||||
| 		sec, err := Cfg.GetSection("log." + name) | ||||
| 		if err != nil { | ||||
| 			sec, _ = Cfg.NewSection("log." + mode) | ||||
| 			sec, _ = Cfg.NewSection("log." + name) | ||||
| 		} | ||||
| 
 | ||||
| 		// Log level.
 | ||||
| 		levelName := getLogLevel("log."+mode, "LEVEL", LogLevel) | ||||
| 		level, ok := logLevels[levelName] | ||||
| 		if !ok { | ||||
| 			log.Fatal(4, "Unknown log level: %s", levelName) | ||||
| 		} | ||||
| 
 | ||||
| 		// Generate log configuration.
 | ||||
| 		switch mode { | ||||
| 		case "console": | ||||
| 			LogConfigs[i] = fmt.Sprintf(`{"level":%s}`, level) | ||||
| 		case "file": | ||||
| 			logPath := sec.Key("FILE_NAME").MustString(path.Join(LogRootPath, "gitea.log")) | ||||
| 			if err = os.MkdirAll(path.Dir(logPath), os.ModePerm); err != nil { | ||||
| 				panic(err.Error()) | ||||
| 			} | ||||
| 
 | ||||
| 			LogConfigs[i] = fmt.Sprintf( | ||||
| 				`{"level":%s,"filename":"%s","rotate":%v,"maxsize":%d,"daily":%v,"maxdays":%d}`, level, | ||||
| 				logPath, | ||||
| 				sec.Key("LOG_ROTATE").MustBool(true), | ||||
| 				1<<uint(sec.Key("MAX_SIZE_SHIFT").MustInt(28)), | ||||
| 				sec.Key("DAILY_ROTATE").MustBool(true), | ||||
| 				sec.Key("MAX_DAYS").MustInt(7)) | ||||
| 		case "conn": | ||||
| 			LogConfigs[i] = fmt.Sprintf(`{"level":%s,"reconnectOnMsg":%v,"reconnect":%v,"net":"%s","addr":"%s"}`, level, | ||||
| 				sec.Key("RECONNECT_ON_MSG").MustBool(), | ||||
| 				sec.Key("RECONNECT").MustBool(), | ||||
| 				sec.Key("PROTOCOL").In("tcp", []string{"tcp", "unix", "udp"}), | ||||
| 				sec.Key("ADDR").MustString(":7020")) | ||||
| 		case "smtp": | ||||
| 			LogConfigs[i] = fmt.Sprintf(`{"level":%s,"username":"%s","password":"%s","host":"%s","sendTos":["%s"],"subject":"%s"}`, level, | ||||
| 				sec.Key("USER").MustString("example@example.com"), | ||||
| 				sec.Key("PASSWD").MustString("******"), | ||||
| 				sec.Key("HOST").MustString("127.0.0.1:25"), | ||||
| 				strings.Replace(sec.Key("RECEIVERS").MustString("example@example.com"), ",", "\",\"", -1), | ||||
| 				sec.Key("SUBJECT").MustString("Diagnostic message from serve")) | ||||
| 		case "database": | ||||
| 			LogConfigs[i] = fmt.Sprintf(`{"level":%s,"driver":"%s","conn":"%s"}`, level, | ||||
| 				sec.Key("DRIVER").String(), | ||||
| 				sec.Key("CONN").String()) | ||||
| 		} | ||||
| 
 | ||||
| 		log.NewLogger(Cfg.Section("log").Key("BUFFER_LEN").MustInt64(10000), mode, LogConfigs[i]) | ||||
| 		log.Info("Log Mode: %s(%s)", strings.Title(mode), levelName) | ||||
| 		provider, config, levelName := generateLogConfig(sec, name, options) | ||||
| 		log.NewLogger(options.bufferLength, name, provider, config) | ||||
| 		description.SubLogDescriptions = append(description.SubLogDescriptions, SubLogDescription{ | ||||
| 			Name:     name, | ||||
| 			Provider: provider, | ||||
| 			Config:   config, | ||||
| 		}) | ||||
| 		log.Info("Gitea Log Mode: %s(%s:%s)", strings.Title(name), strings.Title(provider), levelName) | ||||
| 	} | ||||
| 
 | ||||
| 	LogDescriptions[log.DEFAULT] = &description | ||||
| 
 | ||||
| 	// Finally redirect the default golog to here
 | ||||
| 	golog.SetFlags(0) | ||||
| 	golog.SetPrefix("") | ||||
| 	golog.SetOutput(log.NewLoggerAsWriter("INFO", log.GetLogger(log.DEFAULT))) | ||||
| } | ||||
| 
 | ||||
| // NewXORMLogService initializes xorm logger service
 | ||||
| func NewXORMLogService(disableConsole bool) { | ||||
| 	logModes := strings.Split(Cfg.Section("log").Key("MODE").MustString("console"), ",") | ||||
| 	var logConfigs string | ||||
| 	for _, mode := range logModes { | ||||
| 		mode = strings.TrimSpace(mode) | ||||
| 	EnableXORMLog = Cfg.Section("log").Key("ENABLE_XORM_LOG").MustBool(true) | ||||
| 	if EnableXORMLog { | ||||
| 		options := newDefaultLogOptions() | ||||
| 		options.filename = filepath.Join(LogRootPath, "xorm.log") | ||||
| 		options.bufferLength = Cfg.Section("log").Key("BUFFER_LEN").MustInt64(10000) | ||||
| 		options.disableConsole = disableConsole | ||||
| 
 | ||||
| 		if disableConsole && mode == "console" { | ||||
| 			continue | ||||
| 		} | ||||
| 
 | ||||
| 		sec, err := Cfg.GetSection("log." + mode) | ||||
| 		if err != nil { | ||||
| 			sec, _ = Cfg.NewSection("log." + mode) | ||||
| 		} | ||||
| 
 | ||||
| 		// Log level.
 | ||||
| 		levelName := getLogLevel("log."+mode, "LEVEL", LogLevel) | ||||
| 		level, ok := logLevels[levelName] | ||||
| 		if !ok { | ||||
| 			log.Fatal(4, "Unknown log level: %s", levelName) | ||||
| 		} | ||||
| 
 | ||||
| 		// Generate log configuration.
 | ||||
| 		switch mode { | ||||
| 		case "console": | ||||
| 			logConfigs = fmt.Sprintf(`{"level":%s}`, level) | ||||
| 		case "file": | ||||
| 			logPath := sec.Key("FILE_NAME").MustString(path.Join(LogRootPath, "xorm.log")) | ||||
| 			if err = os.MkdirAll(path.Dir(logPath), os.ModePerm); err != nil { | ||||
| 				panic(err.Error()) | ||||
| 			} | ||||
| 			logPath = path.Join(filepath.Dir(logPath), "xorm.log") | ||||
| 
 | ||||
| 			logConfigs = fmt.Sprintf( | ||||
| 				`{"level":%s,"filename":"%s","rotate":%v,"maxsize":%d,"daily":%v,"maxdays":%d}`, level, | ||||
| 				logPath, | ||||
| 				sec.Key("LOG_ROTATE").MustBool(true), | ||||
| 				1<<uint(sec.Key("MAX_SIZE_SHIFT").MustInt(28)), | ||||
| 				sec.Key("DAILY_ROTATE").MustBool(true), | ||||
| 				sec.Key("MAX_DAYS").MustInt(7)) | ||||
| 		case "conn": | ||||
| 			logConfigs = fmt.Sprintf(`{"level":%s,"reconnectOnMsg":%v,"reconnect":%v,"net":"%s","addr":"%s"}`, level, | ||||
| 				sec.Key("RECONNECT_ON_MSG").MustBool(), | ||||
| 				sec.Key("RECONNECT").MustBool(), | ||||
| 				sec.Key("PROTOCOL").In("tcp", []string{"tcp", "unix", "udp"}), | ||||
| 				sec.Key("ADDR").MustString(":7020")) | ||||
| 		case "smtp": | ||||
| 			logConfigs = fmt.Sprintf(`{"level":%s,"username":"%s","password":"%s","host":"%s","sendTos":"%s","subject":"%s"}`, level, | ||||
| 				sec.Key("USER").MustString("example@example.com"), | ||||
| 				sec.Key("PASSWD").MustString("******"), | ||||
| 				sec.Key("HOST").MustString("127.0.0.1:25"), | ||||
| 				sec.Key("RECEIVERS").MustString("[]"), | ||||
| 				sec.Key("SUBJECT").MustString("Diagnostic message from serve")) | ||||
| 		case "database": | ||||
| 			logConfigs = fmt.Sprintf(`{"level":%s,"driver":"%s","conn":"%s"}`, level, | ||||
| 				sec.Key("DRIVER").String(), | ||||
| 				sec.Key("CONN").String()) | ||||
| 		} | ||||
| 
 | ||||
| 		log.NewXORMLogger(Cfg.Section("log").Key("BUFFER_LEN").MustInt64(10000), mode, logConfigs) | ||||
| 		if !disableConsole { | ||||
| 			log.Info("XORM Log Mode: %s(%s)", strings.Title(mode), levelName) | ||||
| 		} | ||||
| 
 | ||||
| 		var lvl core.LogLevel | ||||
| 		switch levelName { | ||||
| 		case "Trace", "Debug": | ||||
| 			lvl = core.LOG_DEBUG | ||||
| 		case "Info": | ||||
| 			lvl = core.LOG_INFO | ||||
| 		case "Warn": | ||||
| 			lvl = core.LOG_WARNING | ||||
| 		case "Error", "Critical": | ||||
| 			lvl = core.LOG_ERR | ||||
| 		} | ||||
| 		log.XORMLogger.SetLevel(lvl) | ||||
| 	} | ||||
| 
 | ||||
| 	if len(logConfigs) == 0 { | ||||
| 		log.DiscardXORMLogger() | ||||
| 		Cfg.Section("log").Key("XORM").MustString(",") | ||||
| 		generateNamedLogger("xorm", options) | ||||
| 		log.InitXORMLogger(LogSQL) | ||||
| 	} else { | ||||
| 		log.InitXORMLogger(false) | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -84,7 +84,7 @@ func newMailService() { | |||
| 
 | ||||
| 	parsed, err := mail.ParseAddress(MailService.From) | ||||
| 	if err != nil { | ||||
| 		log.Fatal(4, "Invalid mailer.FROM (%s): %v", MailService.From, err) | ||||
| 		log.Fatal("Invalid mailer.FROM (%s): %v", MailService.From, err) | ||||
| 	} | ||||
| 	MailService.FromName = parsed.Name | ||||
| 	MailService.FromEmail = parsed.Address | ||||
|  | @ -96,7 +96,7 @@ func newMailService() { | |||
| 	if MailService.MailerType == "sendmail" { | ||||
| 		MailService.SendmailArgs, err = shellquote.Split(sec.Key("SENDMAIL_ARGS").String()) | ||||
| 		if err != nil { | ||||
| 			log.Error(4, "Failed to parse Sendmail args: %v", CustomConf, err) | ||||
| 			log.Error("Failed to parse Sendmail args: %s with error %v", CustomConf, err) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
|  |  | |||
|  | @ -132,7 +132,7 @@ var ( | |||
| func newRepository() { | ||||
| 	homeDir, err := com.HomeDir() | ||||
| 	if err != nil { | ||||
| 		log.Fatal(4, "Failed to get home directory: %v", err) | ||||
| 		log.Fatal("Failed to get home directory: %v", err) | ||||
| 	} | ||||
| 	homeDir = strings.Replace(homeDir, "\\", "/", -1) | ||||
| 
 | ||||
|  | @ -151,15 +151,15 @@ func newRepository() { | |||
| 	ScriptType = sec.Key("SCRIPT_TYPE").MustString("bash") | ||||
| 
 | ||||
| 	if err = Cfg.Section("repository").MapTo(&Repository); err != nil { | ||||
| 		log.Fatal(4, "Failed to map Repository settings: %v", err) | ||||
| 		log.Fatal("Failed to map Repository settings: %v", err) | ||||
| 	} else if err = Cfg.Section("repository.editor").MapTo(&Repository.Editor); err != nil { | ||||
| 		log.Fatal(4, "Failed to map Repository.Editor settings: %v", err) | ||||
| 		log.Fatal("Failed to map Repository.Editor settings: %v", err) | ||||
| 	} else if err = Cfg.Section("repository.upload").MapTo(&Repository.Upload); err != nil { | ||||
| 		log.Fatal(4, "Failed to map Repository.Upload settings: %v", err) | ||||
| 		log.Fatal("Failed to map Repository.Upload settings: %v", err) | ||||
| 	} else if err = Cfg.Section("repository.local").MapTo(&Repository.Local); err != nil { | ||||
| 		log.Fatal(4, "Failed to map Repository.Local settings: %v", err) | ||||
| 		log.Fatal("Failed to map Repository.Local settings: %v", err) | ||||
| 	} else if err = Cfg.Section("repository.pull-request").MapTo(&Repository.PullRequest); err != nil { | ||||
| 		log.Fatal(4, "Failed to map Repository.PullRequest settings: %v", err) | ||||
| 		log.Fatal("Failed to map Repository.PullRequest settings: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	if !filepath.IsAbs(Repository.Upload.TempPath) { | ||||
|  |  | |||
|  | @ -7,6 +7,7 @@ package setting | |||
| 
 | ||||
| import ( | ||||
| 	"encoding/base64" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"io/ioutil" | ||||
| 	"net" | ||||
|  | @ -15,7 +16,6 @@ import ( | |||
| 	"os/exec" | ||||
| 	"path" | ||||
| 	"path/filepath" | ||||
| 	"runtime" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 	"time" | ||||
|  | @ -90,7 +90,6 @@ var ( | |||
| 	RedirectOtherPort    bool | ||||
| 	PortToRedirect       string | ||||
| 	OfflineMode          bool | ||||
| 	DisableRouterLog     bool | ||||
| 	CertFile             string | ||||
| 	KeyFile              string | ||||
| 	StaticRootPath       string | ||||
|  | @ -259,10 +258,16 @@ var ( | |||
| 
 | ||||
| 	// Log settings
 | ||||
| 	LogLevel           string | ||||
| 	StacktraceLogLevel string | ||||
| 	LogRootPath        string | ||||
| 	LogModes           []string | ||||
| 	LogConfigs         []string | ||||
| 	LogDescriptions    = make(map[string]*LogDescription) | ||||
| 	RedirectMacaronLog bool | ||||
| 	DisableRouterLog   bool | ||||
| 	RouterLogLevel     log.Level | ||||
| 	RouterLogMode      string | ||||
| 	EnableAccessLog    bool | ||||
| 	AccessLogTemplate  string | ||||
| 	EnableXORMLog      bool | ||||
| 
 | ||||
| 	// Attachment settings
 | ||||
| 	AttachmentPath         string | ||||
|  | @ -398,19 +403,19 @@ func getWorkPath(appPath string) string { | |||
| } | ||||
| 
 | ||||
| func init() { | ||||
| 	IsWindows = runtime.GOOS == "windows" | ||||
| 	log.NewLogger(0, "console", `{"level": 0}`) | ||||
| 	// We can rely on log.CanColorStdout being set properly because modules/log/console_windows.go comes before modules/setting/setting.go lexicographically
 | ||||
| 	log.NewLogger(0, "console", "console", fmt.Sprintf(`{"level": "trace", "colorize": %t, "stacktraceLevel": "none"}`, log.CanColorStdout)) | ||||
| 
 | ||||
| 	var err error | ||||
| 	if AppPath, err = getAppPath(); err != nil { | ||||
| 		log.Fatal(4, "Failed to get app path: %v", err) | ||||
| 		log.Fatal("Failed to get app path: %v", err) | ||||
| 	} | ||||
| 	AppWorkPath = getWorkPath(AppPath) | ||||
| } | ||||
| 
 | ||||
| func forcePathSeparator(path string) { | ||||
| 	if strings.Contains(path, "\\") { | ||||
| 		log.Fatal(4, "Do not use '\\' or '\\\\' in paths, instead, please use '/' in all places") | ||||
| 		log.Fatal("Do not use '\\' or '\\\\' in paths, instead, please use '/' in all places") | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | @ -430,16 +435,16 @@ func IsRunUserMatchCurrentUser(runUser string) (string, bool) { | |||
| func createPIDFile(pidPath string) { | ||||
| 	currentPid := os.Getpid() | ||||
| 	if err := os.MkdirAll(filepath.Dir(pidPath), os.ModePerm); err != nil { | ||||
| 		log.Fatal(4, "Failed to create PID folder: %v", err) | ||||
| 		log.Fatal("Failed to create PID folder: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	file, err := os.Create(pidPath) | ||||
| 	if err != nil { | ||||
| 		log.Fatal(4, "Failed to create PID file: %v", err) | ||||
| 		log.Fatal("Failed to create PID file: %v", err) | ||||
| 	} | ||||
| 	defer file.Close() | ||||
| 	if _, err := file.WriteString(strconv.FormatInt(int64(currentPid), 10)); err != nil { | ||||
| 		log.Fatal(4, "Failed to write PID information: %v", err) | ||||
| 		log.Fatal("Failed to write PID information: %v", err) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | @ -451,12 +456,12 @@ func CheckLFSVersion() { | |||
| 
 | ||||
| 		binVersion, err := git.BinVersion() | ||||
| 		if err != nil { | ||||
| 			log.Fatal(4, "Error retrieving git version: %v", err) | ||||
| 			log.Fatal("Error retrieving git version: %v", err) | ||||
| 		} | ||||
| 
 | ||||
| 		if !version.Compare(binVersion, "2.1.2", ">=") { | ||||
| 			LFS.StartServer = false | ||||
| 			log.Error(4, "LFS server support needs at least Git v2.1.2") | ||||
| 			log.Error("LFS server support needs at least Git v2.1.2") | ||||
| 		} else { | ||||
| 			git.GlobalCommandArgs = append(git.GlobalCommandArgs, "-c", "filter.lfs.required=", | ||||
| 				"-c", "filter.lfs.smudge=", "-c", "filter.lfs.clean=") | ||||
|  | @ -488,7 +493,7 @@ func NewContext() { | |||
| 
 | ||||
| 	if com.IsFile(CustomConf) { | ||||
| 		if err := Cfg.Append(CustomConf); err != nil { | ||||
| 			log.Fatal(4, "Failed to load custom conf '%s': %v", CustomConf, err) | ||||
| 			log.Fatal("Failed to load custom conf '%s': %v", CustomConf, err) | ||||
| 		} | ||||
| 	} else { | ||||
| 		log.Warn("Custom config '%s' not found, ignore this if you're running first time", CustomConf) | ||||
|  | @ -497,14 +502,16 @@ func NewContext() { | |||
| 
 | ||||
| 	homeDir, err := com.HomeDir() | ||||
| 	if err != nil { | ||||
| 		log.Fatal(4, "Failed to get home directory: %v", err) | ||||
| 		log.Fatal("Failed to get home directory: %v", err) | ||||
| 	} | ||||
| 	homeDir = strings.Replace(homeDir, "\\", "/", -1) | ||||
| 
 | ||||
| 	LogLevel = getLogLevel("log", "LEVEL", "Info") | ||||
| 	LogLevel = getLogLevel(Cfg.Section("log"), "LEVEL", "Info") | ||||
| 	StacktraceLogLevel = getStacktraceLogLevel(Cfg.Section("log"), "STACKTRACE_LEVEL", "None") | ||||
| 	LogRootPath = Cfg.Section("log").Key("ROOT_PATH").MustString(path.Join(AppWorkPath, "log")) | ||||
| 	forcePathSeparator(LogRootPath) | ||||
| 	RedirectMacaronLog = Cfg.Section("log").Key("REDIRECT_MACARON_LOG").MustBool(false) | ||||
| 	RouterLogLevel = log.FromString(Cfg.Section("log").Key("ROUTER_LOG_LEVEL").MustString("Info")) | ||||
| 
 | ||||
| 	sec := Cfg.Section("server") | ||||
| 	AppName = Cfg.Section("").Key("APP_NAME").MustString("Gitea: Git with a cup of tea") | ||||
|  | @ -521,7 +528,7 @@ func NewContext() { | |||
| 		UnixSocketPermissionRaw := sec.Key("UNIX_SOCKET_PERMISSION").MustString("666") | ||||
| 		UnixSocketPermissionParsed, err := strconv.ParseUint(UnixSocketPermissionRaw, 8, 32) | ||||
| 		if err != nil || UnixSocketPermissionParsed > 0777 { | ||||
| 			log.Fatal(4, "Failed to parse unixSocketPermission: %s", UnixSocketPermissionRaw) | ||||
| 			log.Fatal("Failed to parse unixSocketPermission: %s", UnixSocketPermissionRaw) | ||||
| 		} | ||||
| 		UnixSocketPermission = uint32(UnixSocketPermissionParsed) | ||||
| 	} | ||||
|  | @ -547,7 +554,7 @@ func NewContext() { | |||
| 	// Check if has app suburl.
 | ||||
| 	url, err := url.Parse(AppURL) | ||||
| 	if err != nil { | ||||
| 		log.Fatal(4, "Invalid ROOT_URL '%s': %s", AppURL, err) | ||||
| 		log.Fatal("Invalid ROOT_URL '%s': %s", AppURL, err) | ||||
| 	} | ||||
| 	// Suburl should start with '/' and end without '/', such as '/{subpath}'.
 | ||||
| 	// This value is empty if site does not have sub-url.
 | ||||
|  | @ -616,7 +623,7 @@ func NewContext() { | |||
| 	} | ||||
| 	SSH.KeyTestPath = os.TempDir() | ||||
| 	if err = Cfg.Section("server").MapTo(&SSH); err != nil { | ||||
| 		log.Fatal(4, "Failed to map SSH settings: %v", err) | ||||
| 		log.Fatal("Failed to map SSH settings: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	SSH.KeygenPath = sec.Key("SSH_KEYGEN_PATH").MustString("ssh-keygen") | ||||
|  | @ -630,9 +637,9 @@ func NewContext() { | |||
| 
 | ||||
| 	if !SSH.Disabled && !SSH.StartBuiltinServer { | ||||
| 		if err := os.MkdirAll(SSH.RootPath, 0700); err != nil { | ||||
| 			log.Fatal(4, "Failed to create '%s': %v", SSH.RootPath, err) | ||||
| 			log.Fatal("Failed to create '%s': %v", SSH.RootPath, err) | ||||
| 		} else if err = os.MkdirAll(SSH.KeyTestPath, 0644); err != nil { | ||||
| 			log.Fatal(4, "Failed to create '%s': %v", SSH.KeyTestPath, err) | ||||
| 			log.Fatal("Failed to create '%s': %v", SSH.KeyTestPath, err) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
|  | @ -650,7 +657,7 @@ func NewContext() { | |||
| 
 | ||||
| 	sec = Cfg.Section("server") | ||||
| 	if err = sec.MapTo(&LFS); err != nil { | ||||
| 		log.Fatal(4, "Failed to map LFS settings: %v", err) | ||||
| 		log.Fatal("Failed to map LFS settings: %v", err) | ||||
| 	} | ||||
| 	LFS.ContentPath = sec.Key("LFS_CONTENT_PATH").MustString(filepath.Join(AppDataPath, "lfs")) | ||||
| 	if !filepath.IsAbs(LFS.ContentPath) { | ||||
|  | @ -661,7 +668,7 @@ func NewContext() { | |||
| 
 | ||||
| 	if LFS.StartServer { | ||||
| 		if err := os.MkdirAll(LFS.ContentPath, 0700); err != nil { | ||||
| 			log.Fatal(4, "Failed to create '%s': %v", LFS.ContentPath, err) | ||||
| 			log.Fatal("Failed to create '%s': %v", LFS.ContentPath, err) | ||||
| 		} | ||||
| 
 | ||||
| 		LFS.JWTSecretBytes = make([]byte, 32) | ||||
|  | @ -670,7 +677,7 @@ func NewContext() { | |||
| 		if err != nil || n != 32 { | ||||
| 			LFS.JWTSecretBase64, err = generate.NewJwtSecret() | ||||
| 			if err != nil { | ||||
| 				log.Fatal(4, "Error generating JWT Secret for custom config: %v", err) | ||||
| 				log.Fatal("Error generating JWT Secret for custom config: %v", err) | ||||
| 				return | ||||
| 			} | ||||
| 
 | ||||
|  | @ -679,24 +686,24 @@ func NewContext() { | |||
| 			if com.IsFile(CustomConf) { | ||||
| 				// Keeps custom settings if there is already something.
 | ||||
| 				if err := cfg.Append(CustomConf); err != nil { | ||||
| 					log.Error(4, "Failed to load custom conf '%s': %v", CustomConf, err) | ||||
| 					log.Error("Failed to load custom conf '%s': %v", CustomConf, err) | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			cfg.Section("server").Key("LFS_JWT_SECRET").SetValue(LFS.JWTSecretBase64) | ||||
| 
 | ||||
| 			if err := os.MkdirAll(filepath.Dir(CustomConf), os.ModePerm); err != nil { | ||||
| 				log.Fatal(4, "Failed to create '%s': %v", CustomConf, err) | ||||
| 				log.Fatal("Failed to create '%s': %v", CustomConf, err) | ||||
| 			} | ||||
| 			if err := cfg.SaveTo(CustomConf); err != nil { | ||||
| 				log.Fatal(4, "Error saving generated JWT Secret to custom config: %v", err) | ||||
| 				log.Fatal("Error saving generated JWT Secret to custom config: %v", err) | ||||
| 				return | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if err = Cfg.Section("oauth2").MapTo(&OAuth2); err != nil { | ||||
| 		log.Fatal(4, "Failed to OAuth2 settings: %v", err) | ||||
| 		log.Fatal("Failed to OAuth2 settings: %v", err) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
|  | @ -707,24 +714,24 @@ func NewContext() { | |||
| 		if err != nil || n != 32 { | ||||
| 			OAuth2.JWTSecretBase64, err = generate.NewJwtSecret() | ||||
| 			if err != nil { | ||||
| 				log.Fatal(4, "error generating JWT secret: %v", err) | ||||
| 				log.Fatal("error generating JWT secret: %v", err) | ||||
| 				return | ||||
| 			} | ||||
| 			cfg := ini.Empty() | ||||
| 			if com.IsFile(CustomConf) { | ||||
| 				if err := cfg.Append(CustomConf); err != nil { | ||||
| 					log.Error(4, "failed to load custom conf %s: %v", CustomConf, err) | ||||
| 					log.Error("failed to load custom conf %s: %v", CustomConf, err) | ||||
| 					return | ||||
| 				} | ||||
| 			} | ||||
| 			cfg.Section("oauth2").Key("JWT_SECRET").SetValue(OAuth2.JWTSecretBase64) | ||||
| 
 | ||||
| 			if err := os.MkdirAll(filepath.Dir(CustomConf), os.ModePerm); err != nil { | ||||
| 				log.Fatal(4, "failed to create '%s': %v", CustomConf, err) | ||||
| 				log.Fatal("failed to create '%s': %v", CustomConf, err) | ||||
| 				return | ||||
| 			} | ||||
| 			if err := cfg.SaveTo(CustomConf); err != nil { | ||||
| 				log.Fatal(4, "error saving generating JWT secret to custom config: %v", err) | ||||
| 				log.Fatal("error saving generating JWT secret to custom config: %v", err) | ||||
| 				return | ||||
| 			} | ||||
| 		} | ||||
|  | @ -780,7 +787,7 @@ func NewContext() { | |||
| 		TimeFormat = TimeFormatKey | ||||
| 		TestTimeFormat, _ := time.Parse(TimeFormat, TimeFormat) | ||||
| 		if TestTimeFormat.Format(time.RFC3339) != "2006-01-02T15:04:05Z" { | ||||
| 			log.Fatal(4, "Can't create time properly, please check your time format has 2006, 01, 02, 15, 04 and 05") | ||||
| 			log.Fatal("Can't create time properly, please check your time format has 2006, 01, 02, 15, 04 and 05") | ||||
| 		} | ||||
| 		log.Trace("Custom TimeFormat: %s", TimeFormat) | ||||
| 	} | ||||
|  | @ -790,7 +797,7 @@ func NewContext() { | |||
| 	if InstallLock { | ||||
| 		currentUser, match := IsRunUserMatchCurrentUser(RunUser) | ||||
| 		if !match { | ||||
| 			log.Fatal(4, "Expect user '%s' but current user is: %s", RunUser, currentUser) | ||||
| 			log.Fatal("Expect user '%s' but current user is: %s", RunUser, currentUser) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
|  | @ -828,7 +835,7 @@ func NewContext() { | |||
| 	if EnableFederatedAvatar || !DisableGravatar { | ||||
| 		GravatarSourceURL, err = url.Parse(GravatarSource) | ||||
| 		if err != nil { | ||||
| 			log.Fatal(4, "Failed to parse Gravatar URL(%s): %v", | ||||
| 			log.Fatal("Failed to parse Gravatar URL(%s): %v", | ||||
| 				GravatarSource, err) | ||||
| 		} | ||||
| 	} | ||||
|  | @ -845,15 +852,15 @@ func NewContext() { | |||
| 	} | ||||
| 
 | ||||
| 	if err = Cfg.Section("ui").MapTo(&UI); err != nil { | ||||
| 		log.Fatal(4, "Failed to map UI settings: %v", err) | ||||
| 		log.Fatal("Failed to map UI settings: %v", err) | ||||
| 	} else if err = Cfg.Section("markdown").MapTo(&Markdown); err != nil { | ||||
| 		log.Fatal(4, "Failed to map Markdown settings: %v", err) | ||||
| 		log.Fatal("Failed to map Markdown settings: %v", err) | ||||
| 	} else if err = Cfg.Section("admin").MapTo(&Admin); err != nil { | ||||
| 		log.Fatal(4, "Fail to map Admin settings: %v", err) | ||||
| 		log.Fatal("Fail to map Admin settings: %v", err) | ||||
| 	} else if err = Cfg.Section("api").MapTo(&API); err != nil { | ||||
| 		log.Fatal(4, "Failed to map API settings: %v", err) | ||||
| 		log.Fatal("Failed to map API settings: %v", err) | ||||
| 	} else if err = Cfg.Section("metrics").MapTo(&Metrics); err != nil { | ||||
| 		log.Fatal(4, "Failed to map Metrics settings: %v", err) | ||||
| 		log.Fatal("Failed to map Metrics settings: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	newCron() | ||||
|  | @ -909,35 +916,35 @@ func loadInternalToken(sec *ini.Section) string { | |||
| 	} | ||||
| 	tempURI, err := url.Parse(uri) | ||||
| 	if err != nil { | ||||
| 		log.Fatal(4, "Failed to parse INTERNAL_TOKEN_URI (%s): %v", uri, err) | ||||
| 		log.Fatal("Failed to parse INTERNAL_TOKEN_URI (%s): %v", uri, err) | ||||
| 	} | ||||
| 	switch tempURI.Scheme { | ||||
| 	case "file": | ||||
| 		fp, err := os.OpenFile(tempURI.RequestURI(), os.O_RDWR, 0600) | ||||
| 		if err != nil { | ||||
| 			log.Fatal(4, "Failed to open InternalTokenURI (%s): %v", uri, err) | ||||
| 			log.Fatal("Failed to open InternalTokenURI (%s): %v", uri, err) | ||||
| 		} | ||||
| 		defer fp.Close() | ||||
| 
 | ||||
| 		buf, err := ioutil.ReadAll(fp) | ||||
| 		if err != nil { | ||||
| 			log.Fatal(4, "Failed to read InternalTokenURI (%s): %v", uri, err) | ||||
| 			log.Fatal("Failed to read InternalTokenURI (%s): %v", uri, err) | ||||
| 		} | ||||
| 		// No token in the file, generate one and store it.
 | ||||
| 		if len(buf) == 0 { | ||||
| 			token, err := generate.NewInternalToken() | ||||
| 			if err != nil { | ||||
| 				log.Fatal(4, "Error generate internal token: %v", err) | ||||
| 				log.Fatal("Error generate internal token: %v", err) | ||||
| 			} | ||||
| 			if _, err := io.WriteString(fp, token); err != nil { | ||||
| 				log.Fatal(4, "Error writing to InternalTokenURI (%s): %v", uri, err) | ||||
| 				log.Fatal("Error writing to InternalTokenURI (%s): %v", uri, err) | ||||
| 			} | ||||
| 			return token | ||||
| 		} | ||||
| 
 | ||||
| 		return string(buf) | ||||
| 	default: | ||||
| 		log.Fatal(4, "Unsupported URI-Scheme %q (INTERNAL_TOKEN_URI = %q)", tempURI.Scheme, uri) | ||||
| 		log.Fatal("Unsupported URI-Scheme %q (INTERNAL_TOKEN_URI = %q)", tempURI.Scheme, uri) | ||||
| 	} | ||||
| 	return "" | ||||
| } | ||||
|  | @ -948,7 +955,7 @@ func loadOrGenerateInternalToken(sec *ini.Section) string { | |||
| 	if len(token) == 0 { | ||||
| 		token, err = generate.NewInternalToken() | ||||
| 		if err != nil { | ||||
| 			log.Fatal(4, "Error generate internal token: %v", err) | ||||
| 			log.Fatal("Error generate internal token: %v", err) | ||||
| 		} | ||||
| 
 | ||||
| 		// Save secret
 | ||||
|  | @ -956,17 +963,17 @@ func loadOrGenerateInternalToken(sec *ini.Section) string { | |||
| 		if com.IsFile(CustomConf) { | ||||
| 			// Keeps custom settings if there is already something.
 | ||||
| 			if err := cfgSave.Append(CustomConf); err != nil { | ||||
| 				log.Error(4, "Failed to load custom conf '%s': %v", CustomConf, err) | ||||
| 				log.Error("Failed to load custom conf '%s': %v", CustomConf, err) | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		cfgSave.Section("security").Key("INTERNAL_TOKEN").SetValue(token) | ||||
| 
 | ||||
| 		if err := os.MkdirAll(filepath.Dir(CustomConf), os.ModePerm); err != nil { | ||||
| 			log.Fatal(4, "Failed to create '%s': %v", CustomConf, err) | ||||
| 			log.Fatal("Failed to create '%s': %v", CustomConf, err) | ||||
| 		} | ||||
| 		if err := cfgSave.SaveTo(CustomConf); err != nil { | ||||
| 			log.Fatal(4, "Error saving generated INTERNAL_TOKEN to custom config: %v", err) | ||||
| 			log.Fatal("Error saving generated INTERNAL_TOKEN to custom config: %v", err) | ||||
| 		} | ||||
| 	} | ||||
| 	return token | ||||
|  | @ -976,6 +983,9 @@ func loadOrGenerateInternalToken(sec *ini.Section) string { | |||
| func NewServices() { | ||||
| 	newService() | ||||
| 	newLogService() | ||||
| 	newMacaronLogService() | ||||
| 	newAccessLogService() | ||||
| 	newRouterLogService() | ||||
| 	NewXORMLogService(false) | ||||
| 	newCacheService() | ||||
| 	newSessionService() | ||||
|  |  | |||
|  | @ -43,7 +43,7 @@ func handleServerConn(keyID string, chans <-chan ssh.NewChannel) { | |||
| 
 | ||||
| 		ch, reqs, err := newChan.Accept() | ||||
| 		if err != nil { | ||||
| 			log.Error(3, "Error accepting channel: %v", err) | ||||
| 			log.Error("Error accepting channel: %v", err) | ||||
| 			continue | ||||
| 		} | ||||
| 
 | ||||
|  | @ -61,7 +61,7 @@ func handleServerConn(keyID string, chans <-chan ssh.NewChannel) { | |||
| 					args[0] = strings.TrimLeft(args[0], "\x04") | ||||
| 					_, _, err := com.ExecCmdBytes("env", args[0]+"="+args[1]) | ||||
| 					if err != nil { | ||||
| 						log.Error(3, "env: %v", err) | ||||
| 						log.Error("env: %v", err) | ||||
| 						return | ||||
| 					} | ||||
| 				case "exec": | ||||
|  | @ -79,23 +79,23 @@ func handleServerConn(keyID string, chans <-chan ssh.NewChannel) { | |||
| 
 | ||||
| 					stdout, err := cmd.StdoutPipe() | ||||
| 					if err != nil { | ||||
| 						log.Error(3, "SSH: StdoutPipe: %v", err) | ||||
| 						log.Error("SSH: StdoutPipe: %v", err) | ||||
| 						return | ||||
| 					} | ||||
| 					stderr, err := cmd.StderrPipe() | ||||
| 					if err != nil { | ||||
| 						log.Error(3, "SSH: StderrPipe: %v", err) | ||||
| 						log.Error("SSH: StderrPipe: %v", err) | ||||
| 						return | ||||
| 					} | ||||
| 					input, err := cmd.StdinPipe() | ||||
| 					if err != nil { | ||||
| 						log.Error(3, "SSH: StdinPipe: %v", err) | ||||
| 						log.Error("SSH: StdinPipe: %v", err) | ||||
| 						return | ||||
| 					} | ||||
| 
 | ||||
| 					// FIXME: check timeout
 | ||||
| 					if err = cmd.Start(); err != nil { | ||||
| 						log.Error(3, "SSH: Start: %v", err) | ||||
| 						log.Error("SSH: Start: %v", err) | ||||
| 						return | ||||
| 					} | ||||
| 
 | ||||
|  | @ -105,7 +105,7 @@ func handleServerConn(keyID string, chans <-chan ssh.NewChannel) { | |||
| 					io.Copy(ch.Stderr(), stderr) | ||||
| 
 | ||||
| 					if err = cmd.Wait(); err != nil { | ||||
| 						log.Error(3, "SSH: Wait: %v", err) | ||||
| 						log.Error("SSH: Wait: %v", err) | ||||
| 						return | ||||
| 					} | ||||
| 
 | ||||
|  | @ -121,13 +121,13 @@ func handleServerConn(keyID string, chans <-chan ssh.NewChannel) { | |||
| func listen(config *ssh.ServerConfig, host string, port int) { | ||||
| 	listener, err := net.Listen("tcp", host+":"+com.ToStr(port)) | ||||
| 	if err != nil { | ||||
| 		log.Fatal(4, "Failed to start SSH server: %v", err) | ||||
| 		log.Fatal("Failed to start SSH server: %v", err) | ||||
| 	} | ||||
| 	for { | ||||
| 		// Once a ServerConfig has been configured, connections can be accepted.
 | ||||
| 		conn, err := listener.Accept() | ||||
| 		if err != nil { | ||||
| 			log.Error(3, "SSH: Error accepting incoming connection: %v", err) | ||||
| 			log.Error("SSH: Error accepting incoming connection: %v", err) | ||||
| 			continue | ||||
| 		} | ||||
| 
 | ||||
|  | @ -142,7 +142,7 @@ func listen(config *ssh.ServerConfig, host string, port int) { | |||
| 				if err == io.EOF { | ||||
| 					log.Warn("SSH: Handshaking with %s was terminated: %v", conn.RemoteAddr(), err) | ||||
| 				} else { | ||||
| 					log.Error(3, "SSH: Error on handshaking with %s: %v", conn.RemoteAddr(), err) | ||||
| 					log.Error("SSH: Error on handshaking with %s: %v", conn.RemoteAddr(), err) | ||||
| 				} | ||||
| 				return | ||||
| 			} | ||||
|  | @ -166,7 +166,7 @@ func Listen(host string, port int, ciphers []string, keyExchanges []string, macs | |||
| 		PublicKeyCallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) { | ||||
| 			pkey, err := models.SearchPublicKeyByContent(strings.TrimSpace(string(ssh.MarshalAuthorizedKey(key)))) | ||||
| 			if err != nil { | ||||
| 				log.Error(3, "SearchPublicKeyByContent: %v", err) | ||||
| 				log.Error("SearchPublicKeyByContent: %v", err) | ||||
| 				return nil, err | ||||
| 			} | ||||
| 			return &ssh.Permissions{Extensions: map[string]string{"key-id": com.ToStr(pkey.ID)}}, nil | ||||
|  | @ -178,23 +178,23 @@ func Listen(host string, port int, ciphers []string, keyExchanges []string, macs | |||
| 		filePath := filepath.Dir(keyPath) | ||||
| 
 | ||||
| 		if err := os.MkdirAll(filePath, os.ModePerm); err != nil { | ||||
| 			log.Error(4, "Failed to create dir %s: %v", filePath, err) | ||||
| 			log.Error("Failed to create dir %s: %v", filePath, err) | ||||
| 		} | ||||
| 
 | ||||
| 		err := GenKeyPair(keyPath) | ||||
| 		if err != nil { | ||||
| 			log.Fatal(4, "Failed to generate private key: %v", err) | ||||
| 			log.Fatal("Failed to generate private key: %v", err) | ||||
| 		} | ||||
| 		log.Trace("SSH: New private key is generateed: %s", keyPath) | ||||
| 	} | ||||
| 
 | ||||
| 	privateBytes, err := ioutil.ReadFile(keyPath) | ||||
| 	if err != nil { | ||||
| 		log.Fatal(4, "SSH: Failed to load private key") | ||||
| 		log.Fatal("SSH: Failed to load private key") | ||||
| 	} | ||||
| 	private, err := ssh.ParsePrivateKey(privateBytes) | ||||
| 	if err != nil { | ||||
| 		log.Fatal(4, "SSH: Failed to parse private key") | ||||
| 		log.Fatal("SSH: Failed to parse private key") | ||||
| 	} | ||||
| 	config.AddHostKey(private) | ||||
| 
 | ||||
|  |  | |||
|  | @ -351,7 +351,7 @@ func RenderCommitMessageLink(msg, urlPrefix, urlDefault string, metas map[string | |||
| 	// shouldn't be any special HTML.
 | ||||
| 	fullMessage, err := markup.RenderCommitMessage([]byte(cleanMsg), urlPrefix, urlDefault, metas) | ||||
| 	if err != nil { | ||||
| 		log.Error(3, "RenderCommitMessage: %v", err) | ||||
| 		log.Error("RenderCommitMessage: %v", err) | ||||
| 		return "" | ||||
| 	} | ||||
| 	msgLines := strings.Split(strings.TrimSpace(string(fullMessage)), "\n") | ||||
|  | @ -366,7 +366,7 @@ func RenderCommitBody(msg, urlPrefix string, metas map[string]string) template.H | |||
| 	cleanMsg := template.HTMLEscapeString(msg) | ||||
| 	fullMessage, err := markup.RenderCommitMessage([]byte(cleanMsg), urlPrefix, "", metas) | ||||
| 	if err != nil { | ||||
| 		log.Error(3, "RenderCommitMessage: %v", err) | ||||
| 		log.Error("RenderCommitMessage: %v", err) | ||||
| 		return "" | ||||
| 	} | ||||
| 	body := strings.Split(strings.TrimSpace(string(fullMessage)), "\n") | ||||
|  | @ -425,7 +425,7 @@ func ActionIcon(opType models.ActionType) string { | |||
| func ActionContent2Commits(act Actioner) *models.PushCommits { | ||||
| 	push := models.NewPushCommits() | ||||
| 	if err := json.Unmarshal([]byte(act.GetContent()), push); err != nil { | ||||
| 		log.Error(4, "json.Unmarshal:\n%s\nERROR: %v", act.GetContent(), err) | ||||
| 		log.Error("json.Unmarshal:\n%s\nERROR: %v", act.GetContent(), err) | ||||
| 	} | ||||
| 	return push | ||||
| } | ||||
|  |  | |||
|  | @ -30,13 +30,13 @@ func URLJoin(base string, elems ...string) string { | |||
| 	} | ||||
| 	baseURL, err := url.Parse(base) | ||||
| 	if err != nil { | ||||
| 		log.Error(4, "URLJoin: Invalid base URL %s", base) | ||||
| 		log.Error("URLJoin: Invalid base URL %s", base) | ||||
| 		return "" | ||||
| 	} | ||||
| 	joinedPath := path.Join(elems...) | ||||
| 	argURL, err := url.Parse(joinedPath) | ||||
| 	if err != nil { | ||||
| 		log.Error(4, "URLJoin: Invalid arg %s", joinedPath) | ||||
| 		log.Error("URLJoin: Invalid arg %s", joinedPath) | ||||
| 		return "" | ||||
| 	} | ||||
| 	joinedURL := baseURL.ResolveReference(argURL).String() | ||||
|  |  | |||
|  | @ -1742,6 +1742,16 @@ config.git_gc_timeout = GC Operation Timeout | |||
| 
 | ||||
| config.log_config = Log Configuration | ||||
| config.log_mode = Log Mode | ||||
| config.macaron_log_mode = Macaron Log Mode | ||||
| config.own_named_logger = Named Logger | ||||
| config.routes_to_default_logger = Routes To Default Logger | ||||
| config.go_log = Uses Go Log (redirected to default) | ||||
| config.router_log_mode = Router Log Mode | ||||
| config.disabled_logger = Disabled | ||||
| config.access_log_mode = Access Log Mode | ||||
| config.access_log_template = Template | ||||
| config.xorm_log_mode = XORM Log Mode | ||||
| config.xorm_log_sql = Log SQL | ||||
| 
 | ||||
| monitor.cron = Cron Tasks | ||||
| monitor.name = Name | ||||
|  |  | |||
|  | @ -259,11 +259,13 @@ func Config(ctx *context.Context) { | |||
| 	type logger struct { | ||||
| 		Mode, Config string | ||||
| 	} | ||||
| 	loggers := make([]*logger, len(setting.LogModes)) | ||||
| 	for i := range setting.LogModes { | ||||
| 		loggers[i] = &logger{setting.LogModes[i], setting.LogConfigs[i]} | ||||
| 	} | ||||
| 	ctx.Data["Loggers"] = loggers | ||||
| 	ctx.Data["Loggers"] = setting.LogDescriptions | ||||
| 	ctx.Data["RedirectMacaronLog"] = setting.RedirectMacaronLog | ||||
| 	ctx.Data["EnableAccessLog"] = setting.EnableAccessLog | ||||
| 	ctx.Data["AccessLogTemplate"] = setting.AccessLogTemplate | ||||
| 	ctx.Data["DisableRouterLog"] = setting.DisableRouterLog | ||||
| 	ctx.Data["EnableXORMLog"] = setting.EnableXORMLog | ||||
| 	ctx.Data["LogSQL"] = setting.LogSQL | ||||
| 
 | ||||
| 	ctx.HTML(200, tplConfig) | ||||
| } | ||||
|  |  | |||
|  | @ -55,14 +55,14 @@ func ToCommit(repo *models.Repository, c *git.Commit) *api.PayloadCommit { | |||
| 	if author, err := models.GetUserByEmail(c.Author.Email); err == nil { | ||||
| 		authorUsername = author.Name | ||||
| 	} else if !models.IsErrUserNotExist(err) { | ||||
| 		log.Error(4, "GetUserByEmail: %v", err) | ||||
| 		log.Error("GetUserByEmail: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	committerUsername := "" | ||||
| 	if committer, err := models.GetUserByEmail(c.Committer.Email); err == nil { | ||||
| 		committerUsername = committer.Name | ||||
| 	} else if !models.IsErrUserNotExist(err) { | ||||
| 		log.Error(4, "GetUserByEmail: %v", err) | ||||
| 		log.Error("GetUserByEmail: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	verif := models.ParseCommitWithSignature(c) | ||||
|  |  | |||
|  | @ -234,7 +234,7 @@ func CreateUserRepo(ctx *context.APIContext, owner *models.User, opt api.CreateR | |||
| 		} else { | ||||
| 			if repo != nil { | ||||
| 				if err = models.DeleteRepository(ctx.User, ctx.User.ID, repo.ID); err != nil { | ||||
| 					log.Error(4, "DeleteRepository: %v", err) | ||||
| 					log.Error("DeleteRepository: %v", err) | ||||
| 				} | ||||
| 			} | ||||
| 			ctx.Error(500, "CreateRepository", err) | ||||
|  | @ -417,7 +417,7 @@ func Migrate(ctx *context.APIContext, form auth.MigrateRepoForm) { | |||
| 		err = util.URLSanitizedError(err, remoteAddr) | ||||
| 		if repo != nil { | ||||
| 			if errDelete := models.DeleteRepository(ctx.User, ctxUser.ID, repo.ID); errDelete != nil { | ||||
| 				log.Error(4, "DeleteRepository: %v", errDelete) | ||||
| 				log.Error("DeleteRepository: %v", errDelete) | ||||
| 			} | ||||
| 		} | ||||
| 		ctx.Error(500, "MigrateRepository", err) | ||||
|  | @ -597,7 +597,7 @@ func TopicSearch(ctx *context.Context) { | |||
| 		Limit:   10, | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		log.Error(2, "SearchTopics failed: %v", err) | ||||
| 		log.Error("SearchTopics failed: %v", err) | ||||
| 		ctx.JSON(500, map[string]interface{}{ | ||||
| 			"message": "Search topics failed.", | ||||
| 		}) | ||||
|  |  | |||
|  | @ -79,11 +79,11 @@ func GlobalInit() { | |||
| 		if err := initDBEngine(); err == nil { | ||||
| 			log.Info("ORM engine initialization successful!") | ||||
| 		} else { | ||||
| 			log.Fatal(4, "ORM engine initialization failed: %v", err) | ||||
| 			log.Fatal("ORM engine initialization failed: %v", err) | ||||
| 		} | ||||
| 
 | ||||
| 		if err := models.InitOAuth2(); err != nil { | ||||
| 			log.Fatal(4, "Failed to initialize OAuth2 support: %v", err) | ||||
| 			log.Fatal("Failed to initialize OAuth2 support: %v", err) | ||||
| 		} | ||||
| 
 | ||||
| 		models.LoadRepoConfig() | ||||
|  | @ -92,7 +92,7 @@ func GlobalInit() { | |||
| 		// Booting long running goroutines.
 | ||||
| 		cron.NewContext() | ||||
| 		if err := issue_indexer.InitIssueIndexer(false); err != nil { | ||||
| 			log.Fatal(4, "Failed to initialize issue indexer: %v", err) | ||||
| 			log.Fatal("Failed to initialize issue indexer: %v", err) | ||||
| 		} | ||||
| 		models.InitRepoIndexer() | ||||
| 		models.InitSyncMirrors() | ||||
|  |  | |||
|  | @ -236,7 +236,7 @@ func InstallPost(ctx *context.Context, form auth.InstallForm) { | |||
| 	if com.IsFile(setting.CustomConf) { | ||||
| 		// Keeps custom settings if there is already something.
 | ||||
| 		if err = cfg.Append(setting.CustomConf); err != nil { | ||||
| 			log.Error(4, "Failed to load custom conf '%s': %v", setting.CustomConf, err) | ||||
| 			log.Error("Failed to load custom conf '%s': %v", setting.CustomConf, err) | ||||
| 		} | ||||
| 	} | ||||
| 	cfg.Section("database").Key("DB_TYPE").SetValue(models.DbCfg.Type) | ||||
|  |  | |||
|  | @ -78,7 +78,7 @@ func MembersAction(ctx *context.Context) { | |||
| 	} | ||||
| 
 | ||||
| 	if err != nil { | ||||
| 		log.Error(4, "Action(%s): %v", ctx.Params(":action"), err) | ||||
| 		log.Error("Action(%s): %v", ctx.Params(":action"), err) | ||||
| 		ctx.JSON(200, map[string]interface{}{ | ||||
| 			"ok":  false, | ||||
| 			"err": err.Error(), | ||||
|  |  | |||
|  | @ -109,7 +109,7 @@ func TeamsAction(ctx *context.Context) { | |||
| 		if models.IsErrLastOrgOwner(err) { | ||||
| 			ctx.Flash.Error(ctx.Tr("form.last_org_owner")) | ||||
| 		} else { | ||||
| 			log.Error(3, "Action(%s): %v", ctx.Params(":action"), err) | ||||
| 			log.Error("Action(%s): %v", ctx.Params(":action"), err) | ||||
| 			ctx.JSON(200, map[string]interface{}{ | ||||
| 				"ok":  false, | ||||
| 				"err": err.Error(), | ||||
|  | @ -156,7 +156,7 @@ func TeamsRepoAction(ctx *context.Context) { | |||
| 	} | ||||
| 
 | ||||
| 	if err != nil { | ||||
| 		log.Error(3, "Action(%s): '%s' %v", ctx.Params(":action"), ctx.Org.Team.Name, err) | ||||
| 		log.Error("Action(%s): '%s' %v", ctx.Params(":action"), ctx.Org.Team.Name, err) | ||||
| 		ctx.ServerError("TeamsRepoAction", err) | ||||
| 		return | ||||
| 	} | ||||
|  |  | |||
Some files were not shown because too many files have changed in this diff Show more
		Loading…
	
		Reference in a new issue