Pause, Resume, Release&Reopen, Add and Remove Logging from command line (#11777)
* Make LogDescriptions race safe * Add manager commands for pausing, resuming, adding and removing loggers Signed-off-by: Andrew Thornton <art27@cantab.net> * Placate lint * Ensure that file logger is run! * Add support for smtp and conn Signed-off-by: Andrew Thornton <art27@cantab.net> * Add release-and-reopen Signed-off-by: Andrew Thornton <art27@cantab.net> Co-authored-by: techknowlogick <techknowlogick@gitea.io> Co-authored-by: Lauris BH <lauris@nix.lv>
This commit is contained in:
parent
38fb087d19
commit
c5b08f6d5a
17 changed files with 924 additions and 17 deletions
380
cmd/manager.go
380
cmd/manager.go
|
@ -10,6 +10,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/private"
|
"code.gitea.io/gitea/modules/private"
|
||||||
|
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
|
@ -25,16 +26,27 @@ var (
|
||||||
subcmdShutdown,
|
subcmdShutdown,
|
||||||
subcmdRestart,
|
subcmdRestart,
|
||||||
subcmdFlushQueues,
|
subcmdFlushQueues,
|
||||||
|
subcmdLogging,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
subcmdShutdown = cli.Command{
|
subcmdShutdown = cli.Command{
|
||||||
Name: "shutdown",
|
Name: "shutdown",
|
||||||
Usage: "Gracefully shutdown the running process",
|
Usage: "Gracefully shutdown the running process",
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
cli.BoolFlag{
|
||||||
|
Name: "debug",
|
||||||
|
},
|
||||||
|
},
|
||||||
Action: runShutdown,
|
Action: runShutdown,
|
||||||
}
|
}
|
||||||
subcmdRestart = cli.Command{
|
subcmdRestart = cli.Command{
|
||||||
Name: "restart",
|
Name: "restart",
|
||||||
Usage: "Gracefully restart the running process - (not implemented for windows servers)",
|
Usage: "Gracefully restart the running process - (not implemented for windows servers)",
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
cli.BoolFlag{
|
||||||
|
Name: "debug",
|
||||||
|
},
|
||||||
|
},
|
||||||
Action: runRestart,
|
Action: runRestart,
|
||||||
}
|
}
|
||||||
subcmdFlushQueues = cli.Command{
|
subcmdFlushQueues = cli.Command{
|
||||||
|
@ -46,17 +58,331 @@ var (
|
||||||
Name: "timeout",
|
Name: "timeout",
|
||||||
Value: 60 * time.Second,
|
Value: 60 * time.Second,
|
||||||
Usage: "Timeout for the flushing process",
|
Usage: "Timeout for the flushing process",
|
||||||
},
|
}, cli.BoolFlag{
|
||||||
cli.BoolFlag{
|
|
||||||
Name: "non-blocking",
|
Name: "non-blocking",
|
||||||
Usage: "Set to true to not wait for flush to complete before returning",
|
Usage: "Set to true to not wait for flush to complete before returning",
|
||||||
},
|
},
|
||||||
|
cli.BoolFlag{
|
||||||
|
Name: "debug",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
defaultLoggingFlags = []cli.Flag{
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "group, g",
|
||||||
|
Usage: "Group to add logger to - will default to \"default\"",
|
||||||
|
}, cli.StringFlag{
|
||||||
|
Name: "name, n",
|
||||||
|
Usage: "Name of the new logger - will default to mode",
|
||||||
|
}, cli.StringFlag{
|
||||||
|
Name: "level, l",
|
||||||
|
Usage: "Logging level for the new logger",
|
||||||
|
}, cli.StringFlag{
|
||||||
|
Name: "stacktrace-level, L",
|
||||||
|
Usage: "Stacktrace logging level",
|
||||||
|
}, cli.StringFlag{
|
||||||
|
Name: "flags, F",
|
||||||
|
Usage: "Flags for the logger",
|
||||||
|
}, cli.StringFlag{
|
||||||
|
Name: "expression, e",
|
||||||
|
Usage: "Matching expression for the logger",
|
||||||
|
}, cli.StringFlag{
|
||||||
|
Name: "prefix, p",
|
||||||
|
Usage: "Prefix for the logger",
|
||||||
|
}, cli.BoolFlag{
|
||||||
|
Name: "color",
|
||||||
|
Usage: "Use color in the logs",
|
||||||
|
}, cli.BoolFlag{
|
||||||
|
Name: "debug",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
subcmdLogging = cli.Command{
|
||||||
|
Name: "logging",
|
||||||
|
Usage: "Adjust logging commands",
|
||||||
|
Subcommands: []cli.Command{
|
||||||
|
{
|
||||||
|
Name: "pause",
|
||||||
|
Usage: "Pause logging (Gitea will buffer logs up to a certain point and will drop them after that point)",
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
cli.BoolFlag{
|
||||||
|
Name: "debug",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Action: runPauseLogging,
|
||||||
|
}, {
|
||||||
|
Name: "resume",
|
||||||
|
Usage: "Resume logging",
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
cli.BoolFlag{
|
||||||
|
Name: "debug",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Action: runResumeLogging,
|
||||||
|
}, {
|
||||||
|
Name: "release-and-reopen",
|
||||||
|
Usage: "Cause Gitea to release and re-open files used for logging",
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
cli.BoolFlag{
|
||||||
|
Name: "debug",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Action: runReleaseReopenLogging,
|
||||||
|
}, {
|
||||||
|
Name: "remove",
|
||||||
|
Usage: "Remove a logger",
|
||||||
|
ArgsUsage: "[name] Name of logger to remove",
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
cli.BoolFlag{
|
||||||
|
Name: "debug",
|
||||||
|
}, cli.StringFlag{
|
||||||
|
Name: "group, g",
|
||||||
|
Usage: "Group to add logger to - will default to \"default\"",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Action: runRemoveLogger,
|
||||||
|
}, {
|
||||||
|
Name: "add",
|
||||||
|
Usage: "Add a logger",
|
||||||
|
Subcommands: []cli.Command{
|
||||||
|
{
|
||||||
|
Name: "console",
|
||||||
|
Usage: "Add a console logger",
|
||||||
|
Flags: append(defaultLoggingFlags,
|
||||||
|
cli.BoolFlag{
|
||||||
|
Name: "stderr",
|
||||||
|
Usage: "Output console logs to stderr - only relevant for console",
|
||||||
|
}),
|
||||||
|
Action: runAddConsoleLogger,
|
||||||
|
}, {
|
||||||
|
Name: "file",
|
||||||
|
Usage: "Add a file logger",
|
||||||
|
Flags: append(defaultLoggingFlags, []cli.Flag{
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "filename, f",
|
||||||
|
Usage: "Filename for the logger - this must be set.",
|
||||||
|
}, cli.BoolTFlag{
|
||||||
|
Name: "rotate, r",
|
||||||
|
Usage: "Rotate logs",
|
||||||
|
}, cli.Int64Flag{
|
||||||
|
Name: "max-size, s",
|
||||||
|
Usage: "Maximum size in bytes before rotation",
|
||||||
|
}, cli.BoolTFlag{
|
||||||
|
Name: "daily, d",
|
||||||
|
Usage: "Rotate logs daily",
|
||||||
|
}, cli.IntFlag{
|
||||||
|
Name: "max-days, D",
|
||||||
|
Usage: "Maximum number of daily logs to keep",
|
||||||
|
}, cli.BoolTFlag{
|
||||||
|
Name: "compress, z",
|
||||||
|
Usage: "Compress rotated logs",
|
||||||
|
}, cli.IntFlag{
|
||||||
|
Name: "compression-level, Z",
|
||||||
|
Usage: "Compression level to use",
|
||||||
|
},
|
||||||
|
}...),
|
||||||
|
Action: runAddFileLogger,
|
||||||
|
}, {
|
||||||
|
Name: "conn",
|
||||||
|
Usage: "Add a net conn logger",
|
||||||
|
Flags: append(defaultLoggingFlags, []cli.Flag{
|
||||||
|
cli.BoolFlag{
|
||||||
|
Name: "reconnect-on-message, R",
|
||||||
|
Usage: "Reconnect to host for every message",
|
||||||
|
}, cli.BoolFlag{
|
||||||
|
Name: "reconnect, r",
|
||||||
|
Usage: "Reconnect to host when connection is dropped",
|
||||||
|
}, cli.StringFlag{
|
||||||
|
Name: "protocol, P",
|
||||||
|
Usage: "Set protocol to use: tcp, unix, or udp (defaults to tcp)",
|
||||||
|
}, cli.StringFlag{
|
||||||
|
Name: "address, a",
|
||||||
|
Usage: "Host address and port to connect to (defaults to :7020)",
|
||||||
|
},
|
||||||
|
}...),
|
||||||
|
Action: runAddConnLogger,
|
||||||
|
}, {
|
||||||
|
Name: "smtp",
|
||||||
|
Usage: "Add an SMTP logger",
|
||||||
|
Flags: append(defaultLoggingFlags, []cli.Flag{
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "username, u",
|
||||||
|
Usage: "Mail server username",
|
||||||
|
}, cli.StringFlag{
|
||||||
|
Name: "password, P",
|
||||||
|
Usage: "Mail server password",
|
||||||
|
}, cli.StringFlag{
|
||||||
|
Name: "host, H",
|
||||||
|
Usage: "Mail server host (defaults to: 127.0.0.1:25)",
|
||||||
|
}, cli.StringSliceFlag{
|
||||||
|
Name: "send-to, s",
|
||||||
|
Usage: "Email address(es) to send to",
|
||||||
|
}, cli.StringFlag{
|
||||||
|
Name: "subject, S",
|
||||||
|
Usage: "Subject header of sent emails",
|
||||||
|
},
|
||||||
|
}...),
|
||||||
|
Action: runAddSMTPLogger,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func runRemoveLogger(c *cli.Context) error {
|
||||||
|
setup("manager", c.Bool("debug"))
|
||||||
|
group := c.String("group")
|
||||||
|
if len(group) == 0 {
|
||||||
|
group = log.DEFAULT
|
||||||
|
}
|
||||||
|
name := c.Args().First()
|
||||||
|
statusCode, msg := private.RemoveLogger(group, name)
|
||||||
|
switch statusCode {
|
||||||
|
case http.StatusInternalServerError:
|
||||||
|
fail("InternalServerError", msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintln(os.Stdout, msg)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func runAddSMTPLogger(c *cli.Context) error {
|
||||||
|
setup("manager", c.Bool("debug"))
|
||||||
|
vals := map[string]interface{}{}
|
||||||
|
mode := "smtp"
|
||||||
|
if c.IsSet("host") {
|
||||||
|
vals["host"] = c.String("host")
|
||||||
|
} else {
|
||||||
|
vals["host"] = "127.0.0.1:25"
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.IsSet("username") {
|
||||||
|
vals["username"] = c.String("username")
|
||||||
|
}
|
||||||
|
if c.IsSet("password") {
|
||||||
|
vals["password"] = c.String("password")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !c.IsSet("send-to") {
|
||||||
|
return fmt.Errorf("Some recipients must be provided")
|
||||||
|
}
|
||||||
|
vals["sendTos"] = c.StringSlice("send-to")
|
||||||
|
|
||||||
|
if c.IsSet("subject") {
|
||||||
|
vals["subject"] = c.String("subject")
|
||||||
|
} else {
|
||||||
|
vals["subject"] = "Diagnostic message from Gitea"
|
||||||
|
}
|
||||||
|
|
||||||
|
return commonAddLogger(c, mode, vals)
|
||||||
|
}
|
||||||
|
|
||||||
|
func runAddConnLogger(c *cli.Context) error {
|
||||||
|
setup("manager", c.Bool("debug"))
|
||||||
|
vals := map[string]interface{}{}
|
||||||
|
mode := "conn"
|
||||||
|
vals["net"] = "tcp"
|
||||||
|
if c.IsSet("protocol") {
|
||||||
|
switch c.String("protocol") {
|
||||||
|
case "udp":
|
||||||
|
vals["net"] = "udp"
|
||||||
|
case "unix":
|
||||||
|
vals["net"] = "unix"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if c.IsSet("address") {
|
||||||
|
vals["address"] = c.String("address")
|
||||||
|
} else {
|
||||||
|
vals["address"] = ":7020"
|
||||||
|
}
|
||||||
|
if c.IsSet("reconnect") {
|
||||||
|
vals["reconnect"] = c.Bool("reconnect")
|
||||||
|
}
|
||||||
|
if c.IsSet("reconnect-on-message") {
|
||||||
|
vals["reconnectOnMsg"] = c.Bool("reconnect-on-message")
|
||||||
|
}
|
||||||
|
return commonAddLogger(c, mode, vals)
|
||||||
|
}
|
||||||
|
|
||||||
|
func runAddFileLogger(c *cli.Context) error {
|
||||||
|
setup("manager", c.Bool("debug"))
|
||||||
|
vals := map[string]interface{}{}
|
||||||
|
mode := "file"
|
||||||
|
if c.IsSet("filename") {
|
||||||
|
vals["filename"] = c.String("filename")
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("filename must be set when creating a file logger")
|
||||||
|
}
|
||||||
|
if c.IsSet("rotate") {
|
||||||
|
vals["rotate"] = c.Bool("rotate")
|
||||||
|
}
|
||||||
|
if c.IsSet("max-size") {
|
||||||
|
vals["maxsize"] = c.Int64("max-size")
|
||||||
|
}
|
||||||
|
if c.IsSet("daily") {
|
||||||
|
vals["daily"] = c.Bool("daily")
|
||||||
|
}
|
||||||
|
if c.IsSet("max-days") {
|
||||||
|
vals["maxdays"] = c.Int("max-days")
|
||||||
|
}
|
||||||
|
if c.IsSet("compress") {
|
||||||
|
vals["compress"] = c.Bool("compress")
|
||||||
|
}
|
||||||
|
if c.IsSet("compression-level") {
|
||||||
|
vals["compressionLevel"] = c.Int("compression-level")
|
||||||
|
}
|
||||||
|
return commonAddLogger(c, mode, vals)
|
||||||
|
}
|
||||||
|
|
||||||
|
func runAddConsoleLogger(c *cli.Context) error {
|
||||||
|
setup("manager", c.Bool("debug"))
|
||||||
|
vals := map[string]interface{}{}
|
||||||
|
mode := "console"
|
||||||
|
if c.IsSet("stderr") && c.Bool("stderr") {
|
||||||
|
vals["stderr"] = c.Bool("stderr")
|
||||||
|
}
|
||||||
|
return commonAddLogger(c, mode, vals)
|
||||||
|
}
|
||||||
|
|
||||||
|
func commonAddLogger(c *cli.Context, mode string, vals map[string]interface{}) error {
|
||||||
|
if len(c.String("level")) > 0 {
|
||||||
|
vals["level"] = log.FromString(c.String("level")).String()
|
||||||
|
}
|
||||||
|
if len(c.String("stacktrace-level")) > 0 {
|
||||||
|
vals["stacktraceLevel"] = log.FromString(c.String("stacktrace-level")).String()
|
||||||
|
}
|
||||||
|
if len(c.String("expression")) > 0 {
|
||||||
|
vals["expression"] = c.String("expression")
|
||||||
|
}
|
||||||
|
if len(c.String("prefix")) > 0 {
|
||||||
|
vals["prefix"] = c.String("prefix")
|
||||||
|
}
|
||||||
|
if len(c.String("flags")) > 0 {
|
||||||
|
vals["flags"] = log.FlagsFromString(c.String("flags"))
|
||||||
|
}
|
||||||
|
if c.IsSet("color") {
|
||||||
|
vals["colorize"] = c.Bool("color")
|
||||||
|
}
|
||||||
|
group := "default"
|
||||||
|
if c.IsSet("group") {
|
||||||
|
group = c.String("group")
|
||||||
|
}
|
||||||
|
name := mode
|
||||||
|
if c.IsSet("name") {
|
||||||
|
name = c.String("name")
|
||||||
|
}
|
||||||
|
statusCode, msg := private.AddLogger(group, name, mode, vals)
|
||||||
|
switch statusCode {
|
||||||
|
case http.StatusInternalServerError:
|
||||||
|
fail("InternalServerError", msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintln(os.Stdout, msg)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func runShutdown(c *cli.Context) error {
|
func runShutdown(c *cli.Context) error {
|
||||||
setup("manager", false)
|
setup("manager", c.Bool("debug"))
|
||||||
statusCode, msg := private.Shutdown()
|
statusCode, msg := private.Shutdown()
|
||||||
switch statusCode {
|
switch statusCode {
|
||||||
case http.StatusInternalServerError:
|
case http.StatusInternalServerError:
|
||||||
|
@ -68,7 +394,7 @@ func runShutdown(c *cli.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func runRestart(c *cli.Context) error {
|
func runRestart(c *cli.Context) error {
|
||||||
setup("manager", false)
|
setup("manager", c.Bool("debug"))
|
||||||
statusCode, msg := private.Restart()
|
statusCode, msg := private.Restart()
|
||||||
switch statusCode {
|
switch statusCode {
|
||||||
case http.StatusInternalServerError:
|
case http.StatusInternalServerError:
|
||||||
|
@ -80,7 +406,7 @@ func runRestart(c *cli.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func runFlushQueues(c *cli.Context) error {
|
func runFlushQueues(c *cli.Context) error {
|
||||||
setup("manager", false)
|
setup("manager", c.Bool("debug"))
|
||||||
statusCode, msg := private.FlushQueues(c.Duration("timeout"), c.Bool("non-blocking"))
|
statusCode, msg := private.FlushQueues(c.Duration("timeout"), c.Bool("non-blocking"))
|
||||||
switch statusCode {
|
switch statusCode {
|
||||||
case http.StatusInternalServerError:
|
case http.StatusInternalServerError:
|
||||||
|
@ -90,3 +416,39 @@ func runFlushQueues(c *cli.Context) error {
|
||||||
fmt.Fprintln(os.Stdout, msg)
|
fmt.Fprintln(os.Stdout, msg)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func runPauseLogging(c *cli.Context) error {
|
||||||
|
setup("manager", c.Bool("debug"))
|
||||||
|
statusCode, msg := private.PauseLogging()
|
||||||
|
switch statusCode {
|
||||||
|
case http.StatusInternalServerError:
|
||||||
|
fail("InternalServerError", msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintln(os.Stdout, msg)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func runResumeLogging(c *cli.Context) error {
|
||||||
|
setup("manager", c.Bool("debug"))
|
||||||
|
statusCode, msg := private.ResumeLogging()
|
||||||
|
switch statusCode {
|
||||||
|
case http.StatusInternalServerError:
|
||||||
|
fail("InternalServerError", msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintln(os.Stdout, msg)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func runReleaseReopenLogging(c *cli.Context) error {
|
||||||
|
setup("manager", c.Bool("debug"))
|
||||||
|
statusCode, msg := private.ReleaseReopenLogging()
|
||||||
|
switch statusCode {
|
||||||
|
case http.StatusInternalServerError:
|
||||||
|
fail("InternalServerError", msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintln(os.Stdout, msg)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -316,6 +316,28 @@ COLORIZE = true # Or false if your windows terminal cannot color
|
||||||
|
|
||||||
This is equivalent to sending all logs to the console, with default go log being sent to the console log too.
|
This is equivalent to sending all logs to the console, with default go log being sent to the console log too.
|
||||||
|
|
||||||
|
## Releasing-and-Reopening, Pausing and Resuming logging
|
||||||
|
|
||||||
|
If you are running on Unix you may wish to release-and-reopen logs in order to use `logrotate` or other tools.
|
||||||
|
It is possible force gitea to release and reopen it's logging files and connections by sending `SIGUSR1` to the
|
||||||
|
running process, or running `gitea manager logging release-and-reopen`.
|
||||||
|
|
||||||
|
Alternatively, you may wish to pause and resume logging - this can be accomplished through the use of the
|
||||||
|
`gitea manager logging pause` and `gitea manager logging resume` commands. Please note that whilst logging
|
||||||
|
is paused log events below INFO level will not be stored and only a limited number of events will be stored.
|
||||||
|
Logging may block, albeit temporarily, slowing gitea considerably whilst paused - therefore it is
|
||||||
|
recommended that pausing only done for a very short period of time.
|
||||||
|
|
||||||
|
## Adding and removing logging whilst Gitea is running
|
||||||
|
|
||||||
|
It is possible to add and remove logging whilst Gitea is running using the `gitea manager logging add` and `remove` subcommands.
|
||||||
|
This functionality can only adjust running log systems and cannot be used to start the access, macaron or router loggers if they
|
||||||
|
were not already initialised. If you wish to start these systems you are advised to adjust the app.ini and (gracefully) restart
|
||||||
|
the Gitea service.
|
||||||
|
|
||||||
|
The main intention of these commands is to easily add a temporary logger to investigate problems on running systems where a restart
|
||||||
|
may cause the issue to disappear.
|
||||||
|
|
||||||
## Log colorization
|
## Log colorization
|
||||||
|
|
||||||
Logs to the console will be colorized by default when not running on
|
Logs to the console will be colorized by default when not running on
|
||||||
|
|
|
@ -318,3 +318,85 @@ var checklist = []check{
|
||||||
```
|
```
|
||||||
|
|
||||||
This function will receive a command line context and return a list of details about the problems or error.
|
This function will receive a command line context and return a list of details about the problems or error.
|
||||||
|
|
||||||
|
#### manager
|
||||||
|
|
||||||
|
Manage running server operations:
|
||||||
|
|
||||||
|
- Commands:
|
||||||
|
- `shutdown`: Gracefully shutdown the running process
|
||||||
|
- `restart`: Gracefully restart the running process - (not implemented for windows servers)
|
||||||
|
- `flush-queues`: Flush queues in the running process
|
||||||
|
- Options:
|
||||||
|
- `--timeout value`: Timeout for the flushing process (default: 1m0s)
|
||||||
|
- `--non-blocking`: Set to true to not wait for flush to complete before returning
|
||||||
|
- `logging`: Adjust logging commands
|
||||||
|
- Commands:
|
||||||
|
- `pause`: Pause logging
|
||||||
|
- Notes:
|
||||||
|
- The logging level will be raised to INFO temporarily if it is below this level.
|
||||||
|
- Gitea will buffer logs up to a certain point and will drop them after that point.
|
||||||
|
- `resume`: Resume logging
|
||||||
|
- `release-and-reopen`: Cause Gitea to release and re-open files and connections used for logging (Equivalent to sending SIGUSR1 to Gitea.)
|
||||||
|
- `remove name`: Remove the named logger
|
||||||
|
- Options:
|
||||||
|
- `--group group`, `-g group`: Set the group to remove the sublogger from. (defaults to `default`)
|
||||||
|
- `add`: Add a logger
|
||||||
|
- Commands:
|
||||||
|
- `console`: Add a console logger
|
||||||
|
- Options:
|
||||||
|
- `--group value`, `-g value`: Group to add logger to - will default to "default"
|
||||||
|
- `--name value`, `-n value`: Name of the new logger - will default to mode
|
||||||
|
- `--level value`, `-l value`: Logging level for the new logger
|
||||||
|
- `--stacktrace-level value`, `-L value`: Stacktrace logging level
|
||||||
|
- `--flags value`, `-F value`: Flags for the logger
|
||||||
|
- `--expression value`, `-e value`: Matching expression for the logger
|
||||||
|
- `--prefix value`, `-p value`: Prefix for the logger
|
||||||
|
- `--color`: Use color in the logs
|
||||||
|
- `--stderr`: Output console logs to stderr - only relevant for console
|
||||||
|
- `file`: Add a file logger
|
||||||
|
- Options:
|
||||||
|
- `--group value`, `-g value`: Group to add logger to - will default to "default"
|
||||||
|
- `--name value`, `-n value`: Name of the new logger - will default to mode
|
||||||
|
- `--level value`, `-l value`: Logging level for the new logger
|
||||||
|
- `--stacktrace-level value`, `-L value`: Stacktrace logging level
|
||||||
|
- `--flags value`, `-F value`: Flags for the logger
|
||||||
|
- `--expression value`, `-e value`: Matching expression for the logger
|
||||||
|
- `--prefix value`, `-p value`: Prefix for the logger
|
||||||
|
- `--color`: Use color in the logs
|
||||||
|
- `--filename value`, `-f value`: Filename for the logger -
|
||||||
|
- `--rotate`, `-r`: Rotate logs
|
||||||
|
- `--max-size value`, `-s value`: Maximum size in bytes before rotation
|
||||||
|
- `--daily`, `-d`: Rotate logs daily
|
||||||
|
- `--max-days value`, `-D value`: Maximum number of daily logs to keep
|
||||||
|
- `--compress`, `-z`: Compress rotated logs
|
||||||
|
- `--compression-level value`, `-Z value`: Compression level to use
|
||||||
|
- `conn`: Add a network connection logger
|
||||||
|
- Options:
|
||||||
|
- `--group value`, `-g value`: Group to add logger to - will default to "default"
|
||||||
|
- `--name value`, `-n value`: Name of the new logger - will default to mode
|
||||||
|
- `--level value`, `-l value`: Logging level for the new logger
|
||||||
|
- `--stacktrace-level value`, `-L value`: Stacktrace logging level
|
||||||
|
- `--flags value`, `-F value`: Flags for the logger
|
||||||
|
- `--expression value`, `-e value`: Matching expression for the logger
|
||||||
|
- `--prefix value`, `-p value`: Prefix for the logger
|
||||||
|
- `--color`: Use color in the logs
|
||||||
|
- `--reconnect-on-message`, `-R`: Reconnect to host for every message
|
||||||
|
- `--reconnect`, `-r`: Reconnect to host when connection is dropped
|
||||||
|
- `--protocol value`, `-P value`: Set protocol to use: tcp, unix, or udp (defaults to tcp)
|
||||||
|
- `--address value`, `-a value`: Host address and port to connect to (defaults to :7020)
|
||||||
|
- `smtp`: Add an SMTP logger
|
||||||
|
- Options:
|
||||||
|
- `--group value`, `-g value`: Group to add logger to - will default to "default"
|
||||||
|
- `--name value`, `-n value`: Name of the new logger - will default to mode
|
||||||
|
- `--level value`, `-l value`: Logging level for the new logger
|
||||||
|
- `--stacktrace-level value`, `-L value`: Stacktrace logging level
|
||||||
|
- `--flags value`, `-F value`: Flags for the logger
|
||||||
|
- `--expression value`, `-e value`: Matching expression for the logger
|
||||||
|
- `--prefix value`, `-p value`: Prefix for the logger
|
||||||
|
- `--color`: Use color in the logs
|
||||||
|
- `--username value`, `-u value`: Mail server username
|
||||||
|
- `--password value`, `-P value`: Mail server password
|
||||||
|
- `--host value`, `-H value`: Mail server host (defaults to: 127.0.0.1:25)
|
||||||
|
- `--send-to value`, `-s value`: Email address(es) to send to
|
||||||
|
- `--subject value`, `-S value`: Subject header of sent emails
|
||||||
|
|
|
@ -170,6 +170,11 @@ func (log *TestLogger) Init(config string) error {
|
||||||
func (log *TestLogger) Flush() {
|
func (log *TestLogger) Flush() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//ReleaseReopen does nothing
|
||||||
|
func (log *TestLogger) ReleaseReopen() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// GetName returns the default name for this implementation
|
// GetName returns the default name for this implementation
|
||||||
func (log *TestLogger) GetName() string {
|
func (log *TestLogger) GetName() string {
|
||||||
return "test"
|
return "test"
|
||||||
|
|
|
@ -113,7 +113,10 @@ func (g *Manager) handleSignals(ctx context.Context) {
|
||||||
log.Info("PID: %d. Received SIGHUP. Attempting GracefulRestart...", pid)
|
log.Info("PID: %d. Received SIGHUP. Attempting GracefulRestart...", pid)
|
||||||
g.DoGracefulRestart()
|
g.DoGracefulRestart()
|
||||||
case syscall.SIGUSR1:
|
case syscall.SIGUSR1:
|
||||||
log.Info("PID %d. Received SIGUSR1.", pid)
|
log.Warn("PID %d. Received SIGUSR1. Releasing and reopening logs", pid)
|
||||||
|
if err := log.ReleaseReopen(); err != nil {
|
||||||
|
log.Error("Error whilst releasing and reopening logs: %v", err)
|
||||||
|
}
|
||||||
case syscall.SIGUSR2:
|
case syscall.SIGUSR2:
|
||||||
log.Warn("PID %d. Received SIGUSR2. Hammering...", pid)
|
log.Warn("PID %d. Received SIGUSR2. Hammering...", pid)
|
||||||
g.DoImmediateHammer()
|
g.DoImmediateHammer()
|
||||||
|
|
|
@ -77,6 +77,13 @@ func (i *connWriter) connect() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *connWriter) releaseReopen() error {
|
||||||
|
if i.innerWriter != nil {
|
||||||
|
return i.connect()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// ConnLogger implements LoggerProvider.
|
// ConnLogger implements LoggerProvider.
|
||||||
// it writes messages in keep-live tcp connection.
|
// it writes messages in keep-live tcp connection.
|
||||||
type ConnLogger struct {
|
type ConnLogger struct {
|
||||||
|
@ -119,6 +126,11 @@ func (log *ConnLogger) GetName() string {
|
||||||
return "conn"
|
return "conn"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReleaseReopen causes the ConnLogger to reconnect to the server
|
||||||
|
func (log *ConnLogger) ReleaseReopen() error {
|
||||||
|
return log.out.(*connWriter).releaseReopen()
|
||||||
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
Register("conn", NewConn)
|
Register("conn", NewConn)
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,6 +68,20 @@ func (log *ConsoleLogger) Init(config string) error {
|
||||||
func (log *ConsoleLogger) Flush() {
|
func (log *ConsoleLogger) Flush() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReleaseReopen causes the console logger to reconnect to os.Stdout
|
||||||
|
func (log *ConsoleLogger) ReleaseReopen() error {
|
||||||
|
if log.Stderr {
|
||||||
|
log.NewWriterLogger(&nopWriteCloser{
|
||||||
|
w: os.Stderr,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
log.NewWriterLogger(&nopWriteCloser{
|
||||||
|
w: os.Stdout,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// GetName returns the default name for this implementation
|
// GetName returns the default name for this implementation
|
||||||
func (log *ConsoleLogger) GetName() string {
|
func (log *ConsoleLogger) GetName() string {
|
||||||
return "console"
|
return "console"
|
||||||
|
|
|
@ -29,6 +29,7 @@ type EventLogger interface {
|
||||||
GetLevel() Level
|
GetLevel() Level
|
||||||
GetStacktraceLevel() Level
|
GetStacktraceLevel() Level
|
||||||
GetName() string
|
GetName() string
|
||||||
|
ReleaseReopen() error
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChannelledLog represents a cached channel to a LoggerProvider
|
// ChannelledLog represents a cached channel to a LoggerProvider
|
||||||
|
@ -117,6 +118,11 @@ func (l *ChannelledLog) Flush() {
|
||||||
l.flush <- true
|
l.flush <- true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReleaseReopen this ChannelledLog
|
||||||
|
func (l *ChannelledLog) ReleaseReopen() error {
|
||||||
|
return l.loggerProvider.ReleaseReopen()
|
||||||
|
}
|
||||||
|
|
||||||
// GetLevel gets the level of this ChannelledLog
|
// GetLevel gets the level of this ChannelledLog
|
||||||
func (l *ChannelledLog) GetLevel() Level {
|
func (l *ChannelledLog) GetLevel() Level {
|
||||||
return l.loggerProvider.GetLevel()
|
return l.loggerProvider.GetLevel()
|
||||||
|
@ -145,6 +151,7 @@ type MultiChannelledLog struct {
|
||||||
level Level
|
level Level
|
||||||
stacktraceLevel Level
|
stacktraceLevel Level
|
||||||
closed chan bool
|
closed chan bool
|
||||||
|
paused chan bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMultiChannelledLog a new logger instance with given logger provider and config.
|
// NewMultiChannelledLog a new logger instance with given logger provider and config.
|
||||||
|
@ -159,6 +166,7 @@ func NewMultiChannelledLog(name string, bufferLength int64) *MultiChannelledLog
|
||||||
stacktraceLevel: NONE,
|
stacktraceLevel: NONE,
|
||||||
close: make(chan bool),
|
close: make(chan bool),
|
||||||
closed: make(chan bool),
|
closed: make(chan bool),
|
||||||
|
paused: make(chan bool),
|
||||||
}
|
}
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
@ -229,6 +237,33 @@ func (m *MultiChannelledLog) closeLoggers() {
|
||||||
m.closed <- true
|
m.closed <- true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Pause pauses this Logger
|
||||||
|
func (m *MultiChannelledLog) Pause() {
|
||||||
|
m.paused <- true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resume resumes this Logger
|
||||||
|
func (m *MultiChannelledLog) Resume() {
|
||||||
|
m.paused <- false
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReleaseReopen causes this logger to tell its subloggers to release and reopen
|
||||||
|
func (m *MultiChannelledLog) ReleaseReopen() error {
|
||||||
|
m.mutex.Lock()
|
||||||
|
defer m.mutex.Unlock()
|
||||||
|
var accumulatedErr error
|
||||||
|
for _, logger := range m.loggers {
|
||||||
|
if err := logger.ReleaseReopen(); err != nil {
|
||||||
|
if accumulatedErr == nil {
|
||||||
|
accumulatedErr = fmt.Errorf("Error whilst reopening: %s Error: %v", logger.GetName(), err)
|
||||||
|
} else {
|
||||||
|
accumulatedErr = fmt.Errorf("Error whilst reopening: %s Error: %v & %v", logger.GetName(), err, accumulatedErr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return accumulatedErr
|
||||||
|
}
|
||||||
|
|
||||||
// Start processing the MultiChannelledLog
|
// Start processing the MultiChannelledLog
|
||||||
func (m *MultiChannelledLog) Start() {
|
func (m *MultiChannelledLog) Start() {
|
||||||
m.mutex.Lock()
|
m.mutex.Lock()
|
||||||
|
@ -238,8 +273,35 @@ func (m *MultiChannelledLog) Start() {
|
||||||
}
|
}
|
||||||
m.started = true
|
m.started = true
|
||||||
m.mutex.Unlock()
|
m.mutex.Unlock()
|
||||||
|
paused := false
|
||||||
for {
|
for {
|
||||||
|
if paused {
|
||||||
|
select {
|
||||||
|
case paused = <-m.paused:
|
||||||
|
if !paused {
|
||||||
|
m.ResetLevel()
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
select {
|
select {
|
||||||
|
case paused = <-m.paused:
|
||||||
|
if paused && m.level < INFO {
|
||||||
|
m.level = INFO
|
||||||
|
}
|
||||||
case event, ok := <-m.queue:
|
case event, ok := <-m.queue:
|
||||||
if !ok {
|
if !ok {
|
||||||
m.closeLoggers()
|
m.closeLoggers()
|
||||||
|
@ -275,7 +337,7 @@ func (m *MultiChannelledLog) LogEvent(event *Event) error {
|
||||||
select {
|
select {
|
||||||
case m.queue <- event:
|
case m.queue <- event:
|
||||||
return nil
|
return nil
|
||||||
case <-time.After(60 * time.Second):
|
case <-time.After(100 * time.Millisecond):
|
||||||
// We're blocked!
|
// We're blocked!
|
||||||
return ErrTimeout{
|
return ErrTimeout{
|
||||||
Name: m.name,
|
Name: m.name,
|
||||||
|
|
|
@ -249,6 +249,19 @@ func (log *FileLogger) Flush() {
|
||||||
_ = log.mw.fd.Sync()
|
_ = log.mw.fd.Sync()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReleaseReopen releases and reopens log files
|
||||||
|
func (log *FileLogger) ReleaseReopen() error {
|
||||||
|
closingErr := log.mw.fd.Close()
|
||||||
|
startingErr := log.StartLogger()
|
||||||
|
if startingErr != nil {
|
||||||
|
if closingErr != nil {
|
||||||
|
return fmt.Errorf("Error during closing: %v Error during starting: %v", closingErr, startingErr)
|
||||||
|
}
|
||||||
|
return startingErr
|
||||||
|
}
|
||||||
|
return closingErr
|
||||||
|
}
|
||||||
|
|
||||||
// GetName returns the default name for this implementation
|
// GetName returns the default name for this implementation
|
||||||
func (log *FileLogger) GetName() string {
|
func (log *FileLogger) GetName() string {
|
||||||
return "file"
|
return "file"
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
package log
|
package log
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -192,6 +193,42 @@ func IsFatal() bool {
|
||||||
return GetLevel() <= FATAL
|
return GetLevel() <= FATAL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Pause pauses all the loggers
|
||||||
|
func Pause() {
|
||||||
|
NamedLoggers.Range(func(key, value interface{}) bool {
|
||||||
|
logger := value.(*Logger)
|
||||||
|
logger.Pause()
|
||||||
|
logger.Flush()
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resume resumes all the loggers
|
||||||
|
func Resume() {
|
||||||
|
NamedLoggers.Range(func(key, value interface{}) bool {
|
||||||
|
logger := value.(*Logger)
|
||||||
|
logger.Resume()
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReleaseReopen releases and reopens logging files
|
||||||
|
func ReleaseReopen() error {
|
||||||
|
var accumulatedErr error
|
||||||
|
NamedLoggers.Range(func(key, value interface{}) bool {
|
||||||
|
logger := value.(*Logger)
|
||||||
|
if err := logger.ReleaseReopen(); err != nil {
|
||||||
|
if accumulatedErr == nil {
|
||||||
|
accumulatedErr = fmt.Errorf("Error reopening %s: %v", key.(string), err)
|
||||||
|
} else {
|
||||||
|
accumulatedErr = fmt.Errorf("Error reopening %s: %v & %v", key.(string), err, accumulatedErr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
return accumulatedErr
|
||||||
|
}
|
||||||
|
|
||||||
// Close closes all the loggers
|
// Close closes all the loggers
|
||||||
func Close() {
|
func Close() {
|
||||||
l, ok := NamedLoggers.Load(DEFAULT)
|
l, ok := NamedLoggers.Load(DEFAULT)
|
||||||
|
|
|
@ -97,6 +97,11 @@ func (log *SMTPLogger) sendMail(p []byte) (int, error) {
|
||||||
func (log *SMTPLogger) Flush() {
|
func (log *SMTPLogger) Flush() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReleaseReopen does nothing
|
||||||
|
func (log *SMTPLogger) ReleaseReopen() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// GetName returns the default name for this implementation
|
// GetName returns the default name for this implementation
|
||||||
func (log *SMTPLogger) GetName() string {
|
func (log *SMTPLogger) GetName() string {
|
||||||
return "smtp"
|
return "smtp"
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
@ -81,3 +82,110 @@ func FlushQueues(timeout time.Duration, nonBlocking bool) (int, string) {
|
||||||
|
|
||||||
return http.StatusOK, "Flushed"
|
return http.StatusOK, "Flushed"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PauseLogging pauses logging
|
||||||
|
func PauseLogging() (int, string) {
|
||||||
|
reqURL := setting.LocalURL + "api/internal/manager/pause-logging"
|
||||||
|
|
||||||
|
req := newInternalRequest(reqURL, "POST")
|
||||||
|
resp, err := req.Response()
|
||||||
|
if err != nil {
|
||||||
|
return http.StatusInternalServerError, fmt.Sprintf("Unable to contact gitea: %v", err.Error())
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return resp.StatusCode, decodeJSONError(resp).Err
|
||||||
|
}
|
||||||
|
|
||||||
|
return http.StatusOK, "Logging Paused"
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResumeLogging resumes logging
|
||||||
|
func ResumeLogging() (int, string) {
|
||||||
|
reqURL := setting.LocalURL + "api/internal/manager/resume-logging"
|
||||||
|
|
||||||
|
req := newInternalRequest(reqURL, "POST")
|
||||||
|
resp, err := req.Response()
|
||||||
|
if err != nil {
|
||||||
|
return http.StatusInternalServerError, fmt.Sprintf("Unable to contact gitea: %v", err.Error())
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return resp.StatusCode, decodeJSONError(resp).Err
|
||||||
|
}
|
||||||
|
|
||||||
|
return http.StatusOK, "Logging Restarted"
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReleaseReopenLogging releases and reopens logging files
|
||||||
|
func ReleaseReopenLogging() (int, string) {
|
||||||
|
reqURL := setting.LocalURL + "api/internal/manager/release-and-reopen-logging"
|
||||||
|
|
||||||
|
req := newInternalRequest(reqURL, "POST")
|
||||||
|
resp, err := req.Response()
|
||||||
|
if err != nil {
|
||||||
|
return http.StatusInternalServerError, fmt.Sprintf("Unable to contact gitea: %v", err.Error())
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return resp.StatusCode, decodeJSONError(resp).Err
|
||||||
|
}
|
||||||
|
|
||||||
|
return http.StatusOK, "Logging Restarted"
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoggerOptions represents the options for the add logger call
|
||||||
|
type LoggerOptions struct {
|
||||||
|
Group string
|
||||||
|
Name string
|
||||||
|
Mode string
|
||||||
|
Config map[string]interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddLogger adds a logger
|
||||||
|
func AddLogger(group, name, mode string, config map[string]interface{}) (int, string) {
|
||||||
|
reqURL := setting.LocalURL + "api/internal/manager/add-logger"
|
||||||
|
|
||||||
|
req := newInternalRequest(reqURL, "POST")
|
||||||
|
req = req.Header("Content-Type", "application/json")
|
||||||
|
jsonBytes, _ := json.Marshal(LoggerOptions{
|
||||||
|
Group: group,
|
||||||
|
Name: name,
|
||||||
|
Mode: mode,
|
||||||
|
Config: config,
|
||||||
|
})
|
||||||
|
req.Body(jsonBytes)
|
||||||
|
resp, err := req.Response()
|
||||||
|
if err != nil {
|
||||||
|
return http.StatusInternalServerError, fmt.Sprintf("Unable to contact gitea: %v", err.Error())
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return resp.StatusCode, decodeJSONError(resp).Err
|
||||||
|
}
|
||||||
|
|
||||||
|
return http.StatusOK, "Added"
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveLogger removes a logger
|
||||||
|
func RemoveLogger(group, name string) (int, string) {
|
||||||
|
reqURL := setting.LocalURL + fmt.Sprintf("api/internal/manager/remove-logger/%s/%s", url.PathEscape(group), url.PathEscape(name))
|
||||||
|
|
||||||
|
req := newInternalRequest(reqURL, "POST")
|
||||||
|
resp, err := req.Response()
|
||||||
|
if err != nil {
|
||||||
|
return http.StatusInternalServerError, fmt.Sprintf("Unable to contact gitea: %v", err.Error())
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return resp.StatusCode, decodeJSONError(resp).Err
|
||||||
|
}
|
||||||
|
|
||||||
|
return http.StatusOK, "Removed"
|
||||||
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
|
||||||
|
@ -20,6 +21,69 @@ import (
|
||||||
|
|
||||||
var filenameSuffix = ""
|
var filenameSuffix = ""
|
||||||
|
|
||||||
|
var descriptionLock = sync.RWMutex{}
|
||||||
|
var logDescriptions = make(map[string]*LogDescription)
|
||||||
|
|
||||||
|
// GetLogDescriptions returns a race safe set of descriptions
|
||||||
|
func GetLogDescriptions() map[string]*LogDescription {
|
||||||
|
descriptionLock.RLock()
|
||||||
|
defer descriptionLock.RUnlock()
|
||||||
|
descs := make(map[string]*LogDescription, len(logDescriptions))
|
||||||
|
for k, v := range logDescriptions {
|
||||||
|
subLogDescriptions := make([]SubLogDescription, len(v.SubLogDescriptions))
|
||||||
|
for i, s := range v.SubLogDescriptions {
|
||||||
|
subLogDescriptions[i] = s
|
||||||
|
}
|
||||||
|
descs[k] = &LogDescription{
|
||||||
|
Name: v.Name,
|
||||||
|
SubLogDescriptions: subLogDescriptions,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return descs
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddLogDescription adds a set of descriptions to the complete description
|
||||||
|
func AddLogDescription(key string, description *LogDescription) {
|
||||||
|
descriptionLock.Lock()
|
||||||
|
defer descriptionLock.Unlock()
|
||||||
|
logDescriptions[key] = description
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddSubLogDescription adds a sub log description
|
||||||
|
func AddSubLogDescription(key string, subLogDescription SubLogDescription) bool {
|
||||||
|
descriptionLock.Lock()
|
||||||
|
defer descriptionLock.Unlock()
|
||||||
|
desc, ok := logDescriptions[key]
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i, sub := range desc.SubLogDescriptions {
|
||||||
|
if sub.Name == subLogDescription.Name {
|
||||||
|
desc.SubLogDescriptions[i] = subLogDescription
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
desc.SubLogDescriptions = append(desc.SubLogDescriptions, subLogDescription)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveSubLogDescription removes a sub log description
|
||||||
|
func RemoveSubLogDescription(key string, name string) bool {
|
||||||
|
descriptionLock.Lock()
|
||||||
|
defer descriptionLock.Unlock()
|
||||||
|
desc, ok := logDescriptions[key]
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i, sub := range desc.SubLogDescriptions {
|
||||||
|
if sub.Name == name {
|
||||||
|
desc.SubLogDescriptions = append(desc.SubLogDescriptions[:i], desc.SubLogDescriptions[i+1:]...)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
type defaultLogOptions struct {
|
type defaultLogOptions struct {
|
||||||
levelName string // LogLevel
|
levelName string // LogLevel
|
||||||
flags string
|
flags string
|
||||||
|
@ -185,7 +249,7 @@ func generateNamedLogger(key string, options defaultLogOptions) *LogDescription
|
||||||
log.Info("%s Log: %s(%s:%s)", strings.Title(key), strings.Title(name), provider, levelName)
|
log.Info("%s Log: %s(%s:%s)", strings.Title(key), strings.Title(name), provider, levelName)
|
||||||
}
|
}
|
||||||
|
|
||||||
LogDescriptions[key] = &description
|
AddLogDescription(key, &description)
|
||||||
|
|
||||||
return &description
|
return &description
|
||||||
}
|
}
|
||||||
|
@ -279,7 +343,7 @@ func newLogService() {
|
||||||
log.Info("Gitea Log Mode: %s(%s:%s)", strings.Title(name), strings.Title(provider), levelName)
|
log.Info("Gitea Log Mode: %s(%s:%s)", strings.Title(name), strings.Title(provider), levelName)
|
||||||
}
|
}
|
||||||
|
|
||||||
LogDescriptions[log.DEFAULT] = &description
|
AddLogDescription(log.DEFAULT, &description)
|
||||||
|
|
||||||
// Finally redirect the default golog to here
|
// Finally redirect the default golog to here
|
||||||
golog.SetFlags(0)
|
golog.SetFlags(0)
|
||||||
|
|
|
@ -289,7 +289,6 @@ var (
|
||||||
LogLevel string
|
LogLevel string
|
||||||
StacktraceLogLevel string
|
StacktraceLogLevel string
|
||||||
LogRootPath string
|
LogRootPath string
|
||||||
LogDescriptions = make(map[string]*LogDescription)
|
|
||||||
RedirectMacaronLog bool
|
RedirectMacaronLog bool
|
||||||
DisableRouterLog bool
|
DisableRouterLog bool
|
||||||
RouterLogLevel log.Level
|
RouterLogLevel log.Level
|
||||||
|
|
|
@ -307,7 +307,7 @@ func Config(ctx *context.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.Data["EnvVars"] = envVars
|
ctx.Data["EnvVars"] = envVars
|
||||||
ctx.Data["Loggers"] = setting.LogDescriptions
|
ctx.Data["Loggers"] = setting.GetLogDescriptions()
|
||||||
ctx.Data["RedirectMacaronLog"] = setting.RedirectMacaronLog
|
ctx.Data["RedirectMacaronLog"] = setting.RedirectMacaronLog
|
||||||
ctx.Data["EnableAccessLog"] = setting.EnableAccessLog
|
ctx.Data["EnableAccessLog"] = setting.EnableAccessLog
|
||||||
ctx.Data["AccessLogTemplate"] = setting.AccessLogTemplate
|
ctx.Data["AccessLogTemplate"] = setting.AccessLogTemplate
|
||||||
|
|
|
@ -42,6 +42,10 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||||
m.Post("/manager/shutdown", Shutdown)
|
m.Post("/manager/shutdown", Shutdown)
|
||||||
m.Post("/manager/restart", Restart)
|
m.Post("/manager/restart", Restart)
|
||||||
m.Post("/manager/flush-queues", bind(private.FlushOptions{}), FlushQueues)
|
m.Post("/manager/flush-queues", bind(private.FlushOptions{}), FlushQueues)
|
||||||
|
m.Post("/manager/pause-logging", PauseLogging)
|
||||||
|
m.Post("/manager/resume-logging", ResumeLogging)
|
||||||
|
m.Post("/manager/release-and-reopen-logging", ReleaseReopenLogging)
|
||||||
|
m.Post("/manager/add-logger", bind(private.LoggerOptions{}), AddLogger)
|
||||||
|
m.Post("/manager/remove-logger/:group/:name", RemoveLogger)
|
||||||
}, CheckInternalToken)
|
}, CheckInternalToken)
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,12 +5,15 @@
|
||||||
package private
|
package private
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/graceful"
|
"code.gitea.io/gitea/modules/graceful"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/private"
|
"code.gitea.io/gitea/modules/private"
|
||||||
"code.gitea.io/gitea/modules/queue"
|
"code.gitea.io/gitea/modules/queue"
|
||||||
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
|
||||||
"gitea.com/macaron/macaron"
|
"gitea.com/macaron/macaron"
|
||||||
)
|
)
|
||||||
|
@ -34,8 +37,120 @@ func FlushQueues(ctx *macaron.Context, opts private.FlushOptions) {
|
||||||
err := queue.GetManager().FlushAll(ctx.Req.Request.Context(), opts.Timeout)
|
err := queue.GetManager().FlushAll(ctx.Req.Request.Context(), opts.Timeout)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.JSON(http.StatusRequestTimeout, map[string]interface{}{
|
ctx.JSON(http.StatusRequestTimeout, map[string]interface{}{
|
||||||
"err": err,
|
"err": fmt.Sprintf("%v", err),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
ctx.PlainText(http.StatusOK, []byte("success"))
|
ctx.PlainText(http.StatusOK, []byte("success"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PauseLogging pauses logging
|
||||||
|
func PauseLogging(ctx *macaron.Context) {
|
||||||
|
log.Pause()
|
||||||
|
ctx.PlainText(http.StatusOK, []byte("success"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResumeLogging resumes logging
|
||||||
|
func ResumeLogging(ctx *macaron.Context) {
|
||||||
|
log.Resume()
|
||||||
|
ctx.PlainText(http.StatusOK, []byte("success"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReleaseReopenLogging releases and reopens logging files
|
||||||
|
func ReleaseReopenLogging(ctx *macaron.Context) {
|
||||||
|
if err := log.ReleaseReopen(); err != nil {
|
||||||
|
ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
|
||||||
|
"err": fmt.Sprintf("Error during release and reopen: %v", err),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.PlainText(http.StatusOK, []byte("success"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveLogger removes a logger
|
||||||
|
func RemoveLogger(ctx *macaron.Context) {
|
||||||
|
group := ctx.Params("group")
|
||||||
|
name := ctx.Params("name")
|
||||||
|
ok, err := log.GetLogger(group).DelLogger(name)
|
||||||
|
if err != nil {
|
||||||
|
ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
|
||||||
|
"err": fmt.Sprintf("Failed to remove logger: %s %s %v", group, name, err),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if ok {
|
||||||
|
setting.RemoveSubLogDescription(group, name)
|
||||||
|
}
|
||||||
|
ctx.PlainText(http.StatusOK, []byte(fmt.Sprintf("Removed %s %s", group, name)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddLogger adds a logger
|
||||||
|
func AddLogger(ctx *macaron.Context, opts private.LoggerOptions) {
|
||||||
|
if len(opts.Group) == 0 {
|
||||||
|
opts.Group = log.DEFAULT
|
||||||
|
}
|
||||||
|
if _, ok := opts.Config["flags"]; !ok {
|
||||||
|
switch opts.Group {
|
||||||
|
case "access":
|
||||||
|
opts.Config["flags"] = log.FlagsFromString("")
|
||||||
|
case "router":
|
||||||
|
opts.Config["flags"] = log.FlagsFromString("date,time")
|
||||||
|
default:
|
||||||
|
opts.Config["flags"] = log.FlagsFromString("stdflags")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := opts.Config["colorize"]; !ok && opts.Mode == "console" {
|
||||||
|
if _, ok := opts.Config["stderr"]; ok {
|
||||||
|
opts.Config["colorize"] = log.CanColorStderr
|
||||||
|
} else {
|
||||||
|
opts.Config["colorize"] = log.CanColorStdout
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := opts.Config["level"]; !ok {
|
||||||
|
opts.Config["level"] = setting.LogLevel
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := opts.Config["stacktraceLevel"]; !ok {
|
||||||
|
opts.Config["stacktraceLevel"] = setting.StacktraceLogLevel
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.Mode == "file" {
|
||||||
|
if _, ok := opts.Config["maxsize"]; !ok {
|
||||||
|
opts.Config["maxsize"] = 1 << 28
|
||||||
|
}
|
||||||
|
if _, ok := opts.Config["maxdays"]; !ok {
|
||||||
|
opts.Config["maxdays"] = 7
|
||||||
|
}
|
||||||
|
if _, ok := opts.Config["compressionLevel"]; !ok {
|
||||||
|
opts.Config["compressionLevel"] = -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bufferLen := setting.Cfg.Section("log").Key("BUFFER_LEN").MustInt64(10000)
|
||||||
|
byteConfig, err := json.Marshal(opts.Config)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Failed to marshal log configuration: %v %v", opts.Config, err)
|
||||||
|
ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
|
||||||
|
"err": fmt.Sprintf("Failed to marshal log configuration: %v %v", opts.Config, err),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
config := string(byteConfig)
|
||||||
|
|
||||||
|
if err := log.NewNamedLogger(opts.Group, bufferLen, opts.Name, opts.Mode, config); err != nil {
|
||||||
|
log.Error("Failed to create new named logger: %s %v", config, err)
|
||||||
|
ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
|
||||||
|
"err": fmt.Sprintf("Failed to create new named logger: %s %v", config, err),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
setting.AddSubLogDescription(opts.Group, setting.SubLogDescription{
|
||||||
|
Name: opts.Name,
|
||||||
|
Provider: opts.Mode,
|
||||||
|
Config: config,
|
||||||
|
})
|
||||||
|
|
||||||
|
ctx.PlainText(http.StatusOK, []byte("success"))
|
||||||
|
}
|
||||||
|
|
Reference in a new issue