You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

454 lines
11 KiB

  1. // Copyright 2020 The Gitea Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package cmd
  5. import (
  6. "fmt"
  7. "net/http"
  8. "os"
  9. "time"
  10. "code.gitea.io/gitea/modules/log"
  11. "code.gitea.io/gitea/modules/private"
  12. "github.com/urfave/cli"
  13. )
  14. var (
  15. // CmdManager represents the manager command
  16. CmdManager = cli.Command{
  17. Name: "manager",
  18. Usage: "Manage the running gitea process",
  19. Description: "This is a command for managing the running gitea process",
  20. Subcommands: []cli.Command{
  21. subcmdShutdown,
  22. subcmdRestart,
  23. subcmdFlushQueues,
  24. subcmdLogging,
  25. },
  26. }
  27. subcmdShutdown = cli.Command{
  28. Name: "shutdown",
  29. Usage: "Gracefully shutdown the running process",
  30. Flags: []cli.Flag{
  31. cli.BoolFlag{
  32. Name: "debug",
  33. },
  34. },
  35. Action: runShutdown,
  36. }
  37. subcmdRestart = cli.Command{
  38. Name: "restart",
  39. Usage: "Gracefully restart the running process - (not implemented for windows servers)",
  40. Flags: []cli.Flag{
  41. cli.BoolFlag{
  42. Name: "debug",
  43. },
  44. },
  45. Action: runRestart,
  46. }
  47. subcmdFlushQueues = cli.Command{
  48. Name: "flush-queues",
  49. Usage: "Flush queues in the running process",
  50. Action: runFlushQueues,
  51. Flags: []cli.Flag{
  52. cli.DurationFlag{
  53. Name: "timeout",
  54. Value: 60 * time.Second,
  55. Usage: "Timeout for the flushing process",
  56. }, cli.BoolFlag{
  57. Name: "non-blocking",
  58. Usage: "Set to true to not wait for flush to complete before returning",
  59. },
  60. cli.BoolFlag{
  61. Name: "debug",
  62. },
  63. },
  64. }
  65. defaultLoggingFlags = []cli.Flag{
  66. cli.StringFlag{
  67. Name: "group, g",
  68. Usage: "Group to add logger to - will default to \"default\"",
  69. }, cli.StringFlag{
  70. Name: "name, n",
  71. Usage: "Name of the new logger - will default to mode",
  72. }, cli.StringFlag{
  73. Name: "level, l",
  74. Usage: "Logging level for the new logger",
  75. }, cli.StringFlag{
  76. Name: "stacktrace-level, L",
  77. Usage: "Stacktrace logging level",
  78. }, cli.StringFlag{
  79. Name: "flags, F",
  80. Usage: "Flags for the logger",
  81. }, cli.StringFlag{
  82. Name: "expression, e",
  83. Usage: "Matching expression for the logger",
  84. }, cli.StringFlag{
  85. Name: "prefix, p",
  86. Usage: "Prefix for the logger",
  87. }, cli.BoolFlag{
  88. Name: "color",
  89. Usage: "Use color in the logs",
  90. }, cli.BoolFlag{
  91. Name: "debug",
  92. },
  93. }
  94. subcmdLogging = cli.Command{
  95. Name: "logging",
  96. Usage: "Adjust logging commands",
  97. Subcommands: []cli.Command{
  98. {
  99. Name: "pause",
  100. Usage: "Pause logging (Gitea will buffer logs up to a certain point and will drop them after that point)",
  101. Flags: []cli.Flag{
  102. cli.BoolFlag{
  103. Name: "debug",
  104. },
  105. },
  106. Action: runPauseLogging,
  107. }, {
  108. Name: "resume",
  109. Usage: "Resume logging",
  110. Flags: []cli.Flag{
  111. cli.BoolFlag{
  112. Name: "debug",
  113. },
  114. },
  115. Action: runResumeLogging,
  116. }, {
  117. Name: "release-and-reopen",
  118. Usage: "Cause Gitea to release and re-open files used for logging",
  119. Flags: []cli.Flag{
  120. cli.BoolFlag{
  121. Name: "debug",
  122. },
  123. },
  124. Action: runReleaseReopenLogging,
  125. }, {
  126. Name: "remove",
  127. Usage: "Remove a logger",
  128. ArgsUsage: "[name] Name of logger to remove",
  129. Flags: []cli.Flag{
  130. cli.BoolFlag{
  131. Name: "debug",
  132. }, cli.StringFlag{
  133. Name: "group, g",
  134. Usage: "Group to add logger to - will default to \"default\"",
  135. },
  136. },
  137. Action: runRemoveLogger,
  138. }, {
  139. Name: "add",
  140. Usage: "Add a logger",
  141. Subcommands: []cli.Command{
  142. {
  143. Name: "console",
  144. Usage: "Add a console logger",
  145. Flags: append(defaultLoggingFlags,
  146. cli.BoolFlag{
  147. Name: "stderr",
  148. Usage: "Output console logs to stderr - only relevant for console",
  149. }),
  150. Action: runAddConsoleLogger,
  151. }, {
  152. Name: "file",
  153. Usage: "Add a file logger",
  154. Flags: append(defaultLoggingFlags, []cli.Flag{
  155. cli.StringFlag{
  156. Name: "filename, f",
  157. Usage: "Filename for the logger - this must be set.",
  158. }, cli.BoolTFlag{
  159. Name: "rotate, r",
  160. Usage: "Rotate logs",
  161. }, cli.Int64Flag{
  162. Name: "max-size, s",
  163. Usage: "Maximum size in bytes before rotation",
  164. }, cli.BoolTFlag{
  165. Name: "daily, d",
  166. Usage: "Rotate logs daily",
  167. }, cli.IntFlag{
  168. Name: "max-days, D",
  169. Usage: "Maximum number of daily logs to keep",
  170. }, cli.BoolTFlag{
  171. Name: "compress, z",
  172. Usage: "Compress rotated logs",
  173. }, cli.IntFlag{
  174. Name: "compression-level, Z",
  175. Usage: "Compression level to use",
  176. },
  177. }...),
  178. Action: runAddFileLogger,
  179. }, {
  180. Name: "conn",
  181. Usage: "Add a net conn logger",
  182. Flags: append(defaultLoggingFlags, []cli.Flag{
  183. cli.BoolFlag{
  184. Name: "reconnect-on-message, R",
  185. Usage: "Reconnect to host for every message",
  186. }, cli.BoolFlag{
  187. Name: "reconnect, r",
  188. Usage: "Reconnect to host when connection is dropped",
  189. }, cli.StringFlag{
  190. Name: "protocol, P",
  191. Usage: "Set protocol to use: tcp, unix, or udp (defaults to tcp)",
  192. }, cli.StringFlag{
  193. Name: "address, a",
  194. Usage: "Host address and port to connect to (defaults to :7020)",
  195. },
  196. }...),
  197. Action: runAddConnLogger,
  198. }, {
  199. Name: "smtp",
  200. Usage: "Add an SMTP logger",
  201. Flags: append(defaultLoggingFlags, []cli.Flag{
  202. cli.StringFlag{
  203. Name: "username, u",
  204. Usage: "Mail server username",
  205. }, cli.StringFlag{
  206. Name: "password, P",
  207. Usage: "Mail server password",
  208. }, cli.StringFlag{
  209. Name: "host, H",
  210. Usage: "Mail server host (defaults to: 127.0.0.1:25)",
  211. }, cli.StringSliceFlag{
  212. Name: "send-to, s",
  213. Usage: "Email address(es) to send to",
  214. }, cli.StringFlag{
  215. Name: "subject, S",
  216. Usage: "Subject header of sent emails",
  217. },
  218. }...),
  219. Action: runAddSMTPLogger,
  220. },
  221. },
  222. },
  223. },
  224. }
  225. )
  226. func runRemoveLogger(c *cli.Context) error {
  227. setup("manager", c.Bool("debug"))
  228. group := c.String("group")
  229. if len(group) == 0 {
  230. group = log.DEFAULT
  231. }
  232. name := c.Args().First()
  233. statusCode, msg := private.RemoveLogger(group, name)
  234. switch statusCode {
  235. case http.StatusInternalServerError:
  236. fail("InternalServerError", msg)
  237. }
  238. fmt.Fprintln(os.Stdout, msg)
  239. return nil
  240. }
  241. func runAddSMTPLogger(c *cli.Context) error {
  242. setup("manager", c.Bool("debug"))
  243. vals := map[string]interface{}{}
  244. mode := "smtp"
  245. if c.IsSet("host") {
  246. vals["host"] = c.String("host")
  247. } else {
  248. vals["host"] = "127.0.0.1:25"
  249. }
  250. if c.IsSet("username") {
  251. vals["username"] = c.String("username")
  252. }
  253. if c.IsSet("password") {
  254. vals["password"] = c.String("password")
  255. }
  256. if !c.IsSet("send-to") {
  257. return fmt.Errorf("Some recipients must be provided")
  258. }
  259. vals["sendTos"] = c.StringSlice("send-to")
  260. if c.IsSet("subject") {
  261. vals["subject"] = c.String("subject")
  262. } else {
  263. vals["subject"] = "Diagnostic message from Gitea"
  264. }
  265. return commonAddLogger(c, mode, vals)
  266. }
  267. func runAddConnLogger(c *cli.Context) error {
  268. setup("manager", c.Bool("debug"))
  269. vals := map[string]interface{}{}
  270. mode := "conn"
  271. vals["net"] = "tcp"
  272. if c.IsSet("protocol") {
  273. switch c.String("protocol") {
  274. case "udp":
  275. vals["net"] = "udp"
  276. case "unix":
  277. vals["net"] = "unix"
  278. }
  279. }
  280. if c.IsSet("address") {
  281. vals["address"] = c.String("address")
  282. } else {
  283. vals["address"] = ":7020"
  284. }
  285. if c.IsSet("reconnect") {
  286. vals["reconnect"] = c.Bool("reconnect")
  287. }
  288. if c.IsSet("reconnect-on-message") {
  289. vals["reconnectOnMsg"] = c.Bool("reconnect-on-message")
  290. }
  291. return commonAddLogger(c, mode, vals)
  292. }
  293. func runAddFileLogger(c *cli.Context) error {
  294. setup("manager", c.Bool("debug"))
  295. vals := map[string]interface{}{}
  296. mode := "file"
  297. if c.IsSet("filename") {
  298. vals["filename"] = c.String("filename")
  299. } else {
  300. return fmt.Errorf("filename must be set when creating a file logger")
  301. }
  302. if c.IsSet("rotate") {
  303. vals["rotate"] = c.Bool("rotate")
  304. }
  305. if c.IsSet("max-size") {
  306. vals["maxsize"] = c.Int64("max-size")
  307. }
  308. if c.IsSet("daily") {
  309. vals["daily"] = c.Bool("daily")
  310. }
  311. if c.IsSet("max-days") {
  312. vals["maxdays"] = c.Int("max-days")
  313. }
  314. if c.IsSet("compress") {
  315. vals["compress"] = c.Bool("compress")
  316. }
  317. if c.IsSet("compression-level") {
  318. vals["compressionLevel"] = c.Int("compression-level")
  319. }
  320. return commonAddLogger(c, mode, vals)
  321. }
  322. func runAddConsoleLogger(c *cli.Context) error {
  323. setup("manager", c.Bool("debug"))
  324. vals := map[string]interface{}{}
  325. mode := "console"
  326. if c.IsSet("stderr") && c.Bool("stderr") {
  327. vals["stderr"] = c.Bool("stderr")
  328. }
  329. return commonAddLogger(c, mode, vals)
  330. }
  331. func commonAddLogger(c *cli.Context, mode string, vals map[string]interface{}) error {
  332. if len(c.String("level")) > 0 {
  333. vals["level"] = log.FromString(c.String("level")).String()
  334. }
  335. if len(c.String("stacktrace-level")) > 0 {
  336. vals["stacktraceLevel"] = log.FromString(c.String("stacktrace-level")).String()
  337. }
  338. if len(c.String("expression")) > 0 {
  339. vals["expression"] = c.String("expression")
  340. }
  341. if len(c.String("prefix")) > 0 {
  342. vals["prefix"] = c.String("prefix")
  343. }
  344. if len(c.String("flags")) > 0 {
  345. vals["flags"] = log.FlagsFromString(c.String("flags"))
  346. }
  347. if c.IsSet("color") {
  348. vals["colorize"] = c.Bool("color")
  349. }
  350. group := "default"
  351. if c.IsSet("group") {
  352. group = c.String("group")
  353. }
  354. name := mode
  355. if c.IsSet("name") {
  356. name = c.String("name")
  357. }
  358. statusCode, msg := private.AddLogger(group, name, mode, vals)
  359. switch statusCode {
  360. case http.StatusInternalServerError:
  361. fail("InternalServerError", msg)
  362. }
  363. fmt.Fprintln(os.Stdout, msg)
  364. return nil
  365. }
  366. func runShutdown(c *cli.Context) error {
  367. setup("manager", c.Bool("debug"))
  368. statusCode, msg := private.Shutdown()
  369. switch statusCode {
  370. case http.StatusInternalServerError:
  371. fail("InternalServerError", msg)
  372. }
  373. fmt.Fprintln(os.Stdout, msg)
  374. return nil
  375. }
  376. func runRestart(c *cli.Context) error {
  377. setup("manager", c.Bool("debug"))
  378. statusCode, msg := private.Restart()
  379. switch statusCode {
  380. case http.StatusInternalServerError:
  381. fail("InternalServerError", msg)
  382. }
  383. fmt.Fprintln(os.Stdout, msg)
  384. return nil
  385. }
  386. func runFlushQueues(c *cli.Context) error {
  387. setup("manager", c.Bool("debug"))
  388. statusCode, msg := private.FlushQueues(c.Duration("timeout"), c.Bool("non-blocking"))
  389. switch statusCode {
  390. case http.StatusInternalServerError:
  391. fail("InternalServerError", msg)
  392. }
  393. fmt.Fprintln(os.Stdout, msg)
  394. return nil
  395. }
  396. func runPauseLogging(c *cli.Context) error {
  397. setup("manager", c.Bool("debug"))
  398. statusCode, msg := private.PauseLogging()
  399. switch statusCode {
  400. case http.StatusInternalServerError:
  401. fail("InternalServerError", msg)
  402. }
  403. fmt.Fprintln(os.Stdout, msg)
  404. return nil
  405. }
  406. func runResumeLogging(c *cli.Context) error {
  407. setup("manager", c.Bool("debug"))
  408. statusCode, msg := private.ResumeLogging()
  409. switch statusCode {
  410. case http.StatusInternalServerError:
  411. fail("InternalServerError", msg)
  412. }
  413. fmt.Fprintln(os.Stdout, msg)
  414. return nil
  415. }
  416. func runReleaseReopenLogging(c *cli.Context) error {
  417. setup("manager", c.Bool("debug"))
  418. statusCode, msg := private.ReleaseReopenLogging()
  419. switch statusCode {
  420. case http.StatusInternalServerError:
  421. fail("InternalServerError", msg)
  422. }
  423. fmt.Fprintln(os.Stdout, msg)
  424. return nil
  425. }