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"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/private"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
|
@ -25,16 +26,27 @@ var (
|
|||
subcmdShutdown,
|
||||
subcmdRestart,
|
||||
subcmdFlushQueues,
|
||||
subcmdLogging,
|
||||
},
|
||||
}
|
||||
subcmdShutdown = cli.Command{
|
||||
Name: "shutdown",
|
||||
Usage: "Gracefully shutdown the running process",
|
||||
Name: "shutdown",
|
||||
Usage: "Gracefully shutdown the running process",
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "debug",
|
||||
},
|
||||
},
|
||||
Action: runShutdown,
|
||||
}
|
||||
subcmdRestart = cli.Command{
|
||||
Name: "restart",
|
||||
Usage: "Gracefully restart the running process - (not implemented for windows servers)",
|
||||
Name: "restart",
|
||||
Usage: "Gracefully restart the running process - (not implemented for windows servers)",
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "debug",
|
||||
},
|
||||
},
|
||||
Action: runRestart,
|
||||
}
|
||||
subcmdFlushQueues = cli.Command{
|
||||
|
@ -46,17 +58,331 @@ var (
|
|||
Name: "timeout",
|
||||
Value: 60 * time.Second,
|
||||
Usage: "Timeout for the flushing process",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
}, cli.BoolFlag{
|
||||
Name: "non-blocking",
|
||||
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 {
|
||||
setup("manager", false)
|
||||
setup("manager", c.Bool("debug"))
|
||||
statusCode, msg := private.Shutdown()
|
||||
switch statusCode {
|
||||
case http.StatusInternalServerError:
|
||||
|
@ -68,7 +394,7 @@ func runShutdown(c *cli.Context) error {
|
|||
}
|
||||
|
||||
func runRestart(c *cli.Context) error {
|
||||
setup("manager", false)
|
||||
setup("manager", c.Bool("debug"))
|
||||
statusCode, msg := private.Restart()
|
||||
switch statusCode {
|
||||
case http.StatusInternalServerError:
|
||||
|
@ -80,7 +406,7 @@ func runRestart(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"))
|
||||
switch statusCode {
|
||||
case http.StatusInternalServerError:
|
||||
|
@ -90,3 +416,39 @@ func runFlushQueues(c *cli.Context) error {
|
|||
fmt.Fprintln(os.Stdout, msg)
|
||||
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.
|
||||
|
||||
## 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
|
||||
|
||||
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.
|
||||
|
||||
#### 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() {
|
||||
}
|
||||
|
||||
//ReleaseReopen does nothing
|
||||
func (log *TestLogger) ReleaseReopen() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetName returns the default name for this implementation
|
||||
func (log *TestLogger) GetName() string {
|
||||
return "test"
|
||||
|
|
|
@ -113,7 +113,10 @@ func (g *Manager) handleSignals(ctx context.Context) {
|
|||
log.Info("PID: %d. Received SIGHUP. Attempting GracefulRestart...", pid)
|
||||
g.DoGracefulRestart()
|
||||
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:
|
||||
log.Warn("PID %d. Received SIGUSR2. Hammering...", pid)
|
||||
g.DoImmediateHammer()
|
||||
|
|
|
@ -77,6 +77,13 @@ func (i *connWriter) connect() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (i *connWriter) releaseReopen() error {
|
||||
if i.innerWriter != nil {
|
||||
return i.connect()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ConnLogger implements LoggerProvider.
|
||||
// it writes messages in keep-live tcp connection.
|
||||
type ConnLogger struct {
|
||||
|
@ -119,6 +126,11 @@ func (log *ConnLogger) GetName() string {
|
|||
return "conn"
|
||||
}
|
||||
|
||||
// ReleaseReopen causes the ConnLogger to reconnect to the server
|
||||
func (log *ConnLogger) ReleaseReopen() error {
|
||||
return log.out.(*connWriter).releaseReopen()
|
||||
}
|
||||
|
||||
func init() {
|
||||
Register("conn", NewConn)
|
||||
}
|
||||
|
|
|
@ -68,6 +68,20 @@ func (log *ConsoleLogger) Init(config string) error {
|
|||
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
|
||||
func (log *ConsoleLogger) GetName() string {
|
||||
return "console"
|
||||
|
|
|
@ -29,6 +29,7 @@ type EventLogger interface {
|
|||
GetLevel() Level
|
||||
GetStacktraceLevel() Level
|
||||
GetName() string
|
||||
ReleaseReopen() error
|
||||
}
|
||||
|
||||
// ChannelledLog represents a cached channel to a LoggerProvider
|
||||
|
@ -117,6 +118,11 @@ func (l *ChannelledLog) Flush() {
|
|||
l.flush <- true
|
||||
}
|
||||
|
||||
// ReleaseReopen this ChannelledLog
|
||||
func (l *ChannelledLog) ReleaseReopen() error {
|
||||
return l.loggerProvider.ReleaseReopen()
|
||||
}
|
||||
|
||||
// GetLevel gets the level of this ChannelledLog
|
||||
func (l *ChannelledLog) GetLevel() Level {
|
||||
return l.loggerProvider.GetLevel()
|
||||
|
@ -145,6 +151,7 @@ type MultiChannelledLog struct {
|
|||
level Level
|
||||
stacktraceLevel Level
|
||||
closed chan bool
|
||||
paused chan bool
|
||||
}
|
||||
|
||||
// NewMultiChannelledLog a new logger instance with given logger provider and config.
|
||||
|
@ -159,6 +166,7 @@ func NewMultiChannelledLog(name string, bufferLength int64) *MultiChannelledLog
|
|||
stacktraceLevel: NONE,
|
||||
close: make(chan bool),
|
||||
closed: make(chan bool),
|
||||
paused: make(chan bool),
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
@ -229,6 +237,33 @@ func (m *MultiChannelledLog) closeLoggers() {
|
|||
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
|
||||
func (m *MultiChannelledLog) Start() {
|
||||
m.mutex.Lock()
|
||||
|
@ -238,8 +273,35 @@ func (m *MultiChannelledLog) Start() {
|
|||
}
|
||||
m.started = true
|
||||
m.mutex.Unlock()
|
||||
paused := false
|
||||
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 {
|
||||
case paused = <-m.paused:
|
||||
if paused && m.level < INFO {
|
||||
m.level = INFO
|
||||
}
|
||||
case event, ok := <-m.queue:
|
||||
if !ok {
|
||||
m.closeLoggers()
|
||||
|
@ -275,7 +337,7 @@ func (m *MultiChannelledLog) LogEvent(event *Event) error {
|
|||
select {
|
||||
case m.queue <- event:
|
||||
return nil
|
||||
case <-time.After(60 * time.Second):
|
||||
case <-time.After(100 * time.Millisecond):
|
||||
// We're blocked!
|
||||
return ErrTimeout{
|
||||
Name: m.name,
|
||||
|
|
|
@ -249,6 +249,19 @@ func (log *FileLogger) Flush() {
|
|||
_ = 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
|
||||
func (log *FileLogger) GetName() string {
|
||||
return "file"
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
package log
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
@ -192,6 +193,42 @@ func IsFatal() bool {
|
|||
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
|
||||
func Close() {
|
||||
l, ok := NamedLoggers.Load(DEFAULT)
|
||||
|
|
|
@ -97,6 +97,11 @@ func (log *SMTPLogger) sendMail(p []byte) (int, error) {
|
|||
func (log *SMTPLogger) Flush() {
|
||||
}
|
||||
|
||||
// ReleaseReopen does nothing
|
||||
func (log *SMTPLogger) ReleaseReopen() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetName returns the default name for this implementation
|
||||
func (log *SMTPLogger) GetName() string {
|
||||
return "smtp"
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
@ -81,3 +82,110 @@ func FlushQueues(timeout time.Duration, nonBlocking bool) (int, string) {
|
|||
|
||||
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/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
|
||||
|
@ -20,6 +21,69 @@ import (
|
|||
|
||||
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 {
|
||||
levelName string // LogLevel
|
||||
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)
|
||||
}
|
||||
|
||||
LogDescriptions[key] = &description
|
||||
AddLogDescription(key, &description)
|
||||
|
||||
return &description
|
||||
}
|
||||
|
@ -279,7 +343,7 @@ func newLogService() {
|
|||
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
|
||||
golog.SetFlags(0)
|
||||
|
|
|
@ -289,7 +289,6 @@ var (
|
|||
LogLevel string
|
||||
StacktraceLogLevel string
|
||||
LogRootPath string
|
||||
LogDescriptions = make(map[string]*LogDescription)
|
||||
RedirectMacaronLog bool
|
||||
DisableRouterLog bool
|
||||
RouterLogLevel log.Level
|
||||
|
|
|
@ -307,7 +307,7 @@ func Config(ctx *context.Context) {
|
|||
}
|
||||
|
||||
ctx.Data["EnvVars"] = envVars
|
||||
ctx.Data["Loggers"] = setting.LogDescriptions
|
||||
ctx.Data["Loggers"] = setting.GetLogDescriptions()
|
||||
ctx.Data["RedirectMacaronLog"] = setting.RedirectMacaronLog
|
||||
ctx.Data["EnableAccessLog"] = setting.EnableAccessLog
|
||||
ctx.Data["AccessLogTemplate"] = setting.AccessLogTemplate
|
||||
|
|
|
@ -42,6 +42,10 @@ func RegisterRoutes(m *macaron.Macaron) {
|
|||
m.Post("/manager/shutdown", Shutdown)
|
||||
m.Post("/manager/restart", Restart)
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -5,12 +5,15 @@
|
|||
package private
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"code.gitea.io/gitea/modules/graceful"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/private"
|
||||
"code.gitea.io/gitea/modules/queue"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
"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)
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusRequestTimeout, map[string]interface{}{
|
||||
"err": err,
|
||||
"err": fmt.Sprintf("%v", err),
|
||||
})
|
||||
}
|
||||
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