// package qfixpt is a Micro service quantex base project package main import ( "embed" "flag" "fmt" "log/slog" "os" "time" "github.com/mitchellh/panicwrap" "quantex.com/qfixpt/src/app" "quantex.com/qfixpt/src/app/mode" "quantex.com/qfixpt/src/app/version" "quantex.com/qfixpt/src/client/config" googlechat "quantex.com/qfixpt/src/client/notify/google" "quantex.com/qfixpt/src/client/res" "quantex.com/qfixpt/src/cmd" "quantex.com/qfixpt/src/cmd/example" "quantex.com/qfixpt/src/cmd/service" "quantex.com/qfixpt/src/common/logger" "quantex.com/qfixpt/src/common/logger/tint" "quantex.com/qfixpt/src/domain" ) // Embed the entire directory. //go:embed res var resources embed.FS type flagsType struct { runner, globalCfg, serviceCfg, logLevel, logFormat string debug, version bool } func main() { flags := parseFlags() if len(os.Args) == 1 { flag.Usage() return } if flags.version { fmt.Printf("%s version %s", version.AppName, version.Info()) return } cfg, err := config.Read([]string{flags.globalCfg, flags.serviceCfg}) if err != nil { panic(fmt.Sprintf("Something went wrong reading the config. %s", err)) } notify := googlechat.New(cfg.Notify.Google) exitStatus, err := panicwrap.BasicWrap(func(output string) { notify.SendMsg(domain.MessageChannelPanic, output, domain.MessageStatusStopper, nil) os.Exit(1) }) if err != nil { // Something went wrong setting up the panic wrapper. panic(fmt.Sprintf("Something went wrong setting up the panic wrapper. %s", err)) } // If exitStatus >= 0, then we're the parent process and the panicwrap // re-executed ourselves and completed. Just exit with the proper status. if exitStatus >= 0 { os.Exit(exitStatus) } handler, err := newLogHandler(flags) if err != nil { panic(err) } hook := logger.NewQuantexHandler(handler, []slog.Level{slog.LevelError, slog.LevelWarn}) slog.SetDefault(slog.New(hook)) slog.Info("New Run ----------------------------------------------------------------------------") slog.Debug(fmt.Sprintf("flags: %+v", flags)) v := version.AppName + " version " + version.Info() fmt.Println(v) slog.Info(v) setDebugMode(flags.debug) startRunner(flags.runner, flags.globalCfg, flags.serviceCfg) } func parseFlags() (f flagsType) { fmt.Println(f.runner) flag.StringVar(&f.runner, "run", "", "run the specified service") flag.StringVar(&f.globalCfg, "global-cfg", "../global_conf.toml", "set the config global file name") flag.StringVar(&f.serviceCfg, "service-cfg", "conf.toml", "set the config service file name") const u0 = "set the logs output format: text, json, tint1 (one line), tint2 (two lines)" flag.StringVar(&f.logFormat, "logs-format", "json", u0) flag.StringVar(&f.logFormat, "f", "json", u0+" (shorthand)") const u1 = "set the debug mode" flag.BoolVar(&f.debug, "debug", false, u1) flag.BoolVar(&f.debug, "d", false, u1+" (shorthand)") const u2 = "show the program version" flag.BoolVar(&f.version, "version", false, u2) flag.BoolVar(&f.version, "v", false, u2+" (shorthand)") const u3 = "set the log level: debug, info, warn, error" flag.StringVar(&f.logLevel, "level", "info", u3) flag.StringVar(&f.logLevel, "l", "info", u3+" (shorthand)") flag.Parse() return f } func newLogHandler(flags flagsType) (slog.Handler, error) { logLevel, err := parseLogLevel(flags.logLevel) if err != nil { return nil, err } switch flags.logFormat { case "json": return logger.NewJSONHandler(logLevel), nil case "text": return logger.NewTextHandler(logLevel), nil case "tint1": return logger.NewTextHandlerTint(logLevel, tint.OneLine), nil case "tint2": return logger.NewTextHandlerTint(logLevel, tint.TwoLines), nil default: flag.PrintDefaults() return nil, fmt.Errorf("invalid log format option: \"%s\"", flags.logFormat) } } func setDebugMode(debug bool) { mode.Debug = debug var msg string if mode.Debug { res.Set(os.DirFS("./")) msg = fmt.Sprintf("Running %s in DEBUG mode!", version.AppName) } else { res.Set(resources) msg = fmt.Sprintf("Running %s!", version.AppName) } slog.Info(msg) } func parseLogLevel(level string) (slog.Level, error) { switch level { case "debug": return slog.LevelDebug, nil case "info": return slog.LevelInfo, nil case "warn": return slog.LevelWarn, nil case "error": return slog.LevelError, nil default: return 0, fmt.Errorf("invalid log level: %s", level) } } func startRunner(runner, globalCfg, serviceCfg string) { var fn func(cfg app.Config) error if runner == "" { runner = "service" } switch runner { case "service": fn = service.Runner case "async": fn = example.AsyncRunner case "external": fn = example.ExternalRunner case "tracerr": fn = example.TracerrRunner case "logger": fn = example.LogsRunner default: panic("Invalid runner option: \"" + runner + "\"") } if err := cmd.NewRunner(fn)(globalCfg, serviceCfg); err != nil { slog.Error(err.Error()) // Time guard to allow to notify to send the message time.Sleep(2 * time.Second) } }