app.go 3.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. // Package cmd provides the ntfy CLI application
  2. package cmd
  3. import (
  4. "fmt"
  5. "os"
  6. "regexp"
  7. "github.com/urfave/cli/v2"
  8. "github.com/urfave/cli/v2/altsrc"
  9. "heckel.io/ntfy/v2/log"
  10. )
  11. const (
  12. categoryClient = "Client commands"
  13. categoryServer = "Server commands"
  14. )
  15. // Build metadata keys for app.Metadata
  16. const (
  17. MetadataKeyCommit = "commit"
  18. MetadataKeyDate = "date"
  19. )
  20. var commands = make([]*cli.Command, 0)
  21. var flagsDefault = []cli.Flag{
  22. &cli.BoolFlag{Name: "debug", Aliases: []string{"d"}, EnvVars: []string{"NTFY_DEBUG"}, Usage: "enable debug logging"},
  23. &cli.BoolFlag{Name: "trace", EnvVars: []string{"NTFY_TRACE"}, Usage: "enable tracing (very verbose, be careful)"},
  24. &cli.BoolFlag{Name: "no-log-dates", Aliases: []string{"no_log_dates"}, EnvVars: []string{"NTFY_NO_LOG_DATES"}, Usage: "disable the date/time prefix"},
  25. altsrc.NewStringFlag(&cli.StringFlag{Name: "log-level", Aliases: []string{"log_level"}, Value: log.InfoLevel.String(), EnvVars: []string{"NTFY_LOG_LEVEL"}, Usage: "set log level"}),
  26. altsrc.NewStringSliceFlag(&cli.StringSliceFlag{Name: "log-level-overrides", Aliases: []string{"log_level_overrides"}, EnvVars: []string{"NTFY_LOG_LEVEL_OVERRIDES"}, Usage: "set log level overrides"}),
  27. altsrc.NewStringFlag(&cli.StringFlag{Name: "log-format", Aliases: []string{"log_format"}, Value: log.TextFormat.String(), EnvVars: []string{"NTFY_LOG_FORMAT"}, Usage: "set log format"}),
  28. altsrc.NewStringFlag(&cli.StringFlag{Name: "log-file", Aliases: []string{"log_file"}, EnvVars: []string{"NTFY_LOG_FILE"}, Usage: "set log file, default is STDOUT"}),
  29. }
  30. var (
  31. logLevelOverrideRegex = regexp.MustCompile(`(?i)^([^=\s]+)(?:\s*=\s*(\S+))?\s*->\s*(TRACE|DEBUG|INFO|WARN|ERROR)$`)
  32. )
  33. // New creates a new CLI application
  34. func New() *cli.App {
  35. return &cli.App{
  36. Name: "ntfy",
  37. Usage: "Simple pub-sub notification service",
  38. UsageText: "ntfy [OPTION..]",
  39. HideVersion: true,
  40. UseShortOptionHandling: true,
  41. Reader: os.Stdin,
  42. Writer: os.Stdout,
  43. ErrWriter: os.Stderr,
  44. Commands: commands,
  45. Flags: flagsDefault,
  46. Before: initLogFunc,
  47. }
  48. }
  49. func initLogFunc(c *cli.Context) error {
  50. log.SetLevel(log.ToLevel(c.String("log-level")))
  51. log.SetFormat(log.ToFormat(c.String("log-format")))
  52. if c.Bool("trace") {
  53. log.SetLevel(log.TraceLevel)
  54. } else if c.Bool("debug") {
  55. log.SetLevel(log.DebugLevel)
  56. }
  57. if c.Bool("no-log-dates") {
  58. log.DisableDates()
  59. }
  60. if err := applyLogLevelOverrides(c.StringSlice("log-level-overrides")); err != nil {
  61. return err
  62. }
  63. logFile := c.String("log-file")
  64. if logFile != "" {
  65. w, err := os.OpenFile(logFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600)
  66. if err != nil {
  67. return err
  68. }
  69. log.SetOutput(w)
  70. }
  71. return nil
  72. }
  73. func applyLogLevelOverrides(rawOverrides []string) error {
  74. for _, override := range rawOverrides {
  75. m := logLevelOverrideRegex.FindStringSubmatch(override)
  76. if len(m) == 4 {
  77. field, value, level := m[1], m[2], m[3]
  78. log.SetLevelOverride(field, value, log.ToLevel(level))
  79. } else if len(m) == 3 {
  80. field, level := m[1], m[2]
  81. log.SetLevelOverride(field, "", log.ToLevel(level)) // Matches any value
  82. } else {
  83. return fmt.Errorf(`invalid log level override "%s", must be "field=value -> loglevel", e.g. "user_id=u_123 -> DEBUG"`, override)
  84. }
  85. }
  86. return nil
  87. }