package main import ( "context" "flag" "log/slog" "net" "os" "path/filepath" "strings" "kra/internal/app" "kra/internal/conf" "kra/internal/server/router" "kra/internal/service" "kra/internal/service/dto" "kra/internal/worker" "kra/pkg/logging" "kra/pkg/module" "kra/pkg/mq" platformtask "kra/pkg/task" "github.com/go-kratos/kratos/v3" "github.com/go-kratos/kratos/v3/config" "github.com/go-kratos/kratos/v3/config/file" "github.com/go-kratos/kratos/v3/log" kratoshttp "github.com/go-kratos/kratos/v3/transport/http" _ "go.uber.org/automaxprocs" ) // go build -ldflags "-X main.Version=x.y.z" var ( // Name is the name of the compiled software. Name string // Version is the version of the compiled software. Version string // flagconf is the config flag. flagconf string id, _ = os.Hostname() ) func init() { flag.StringVar(&flagconf, "conf", "./configs", "config path, eg: -conf config.yaml") } // runtimeContributions is the binary-level list of modules with constructed // route or task dependencies. Adding another runtime module is explicit here. func runtimeContributions(systemRoutes *router.Routes, systemTasks *worker.TaskMethods) app.RuntimeContributions { return app.RuntimeContributions{ Routes: []module.RouteRegistrar{systemRoutes}, Tasks: []platformtask.Contributor{systemTasks}, } } func newApp(logger *slog.Logger, hs *kratoshttp.Server, scheduler *worker.TaskScheduler, audit *service.AuditRecorder, loggerControl *logging.ReloadableLogger, _ mq.Client) *kratos.App { if audit != nil && loggerControl != nil { loggerControl.SetErrorSink(logging.ErrorSinkFunc(func(ctx context.Context, entry logging.ErrorEntry) error { return audit.CreateErrorRequest(ctx, &dto.ErrorRecordRequest{Form: entry.Form, Info: entry.Info, Level: entry.Level, RequestID: entry.RequestID, TraceID: entry.TraceID}) })) } return kratos.New( kratos.ID(id), kratos.Name(Name), kratos.Version(Version), kratos.Metadata(map[string]string{}), kratos.Logger(logger), kratos.Server( hs, scheduler, ), ) } func zapSettings(admin *conf.AdminBackend) (string, logging.Options) { options := logging.Options{Level: "info", Format: "json", EncodeLevel: "LowercaseLevelEncoder", LogInConsole: true, ShowLine: true, RetentionDay: 7} root := "logs" if admin == nil || admin.Zap == nil { return root, options } zapConfig := admin.Zap options = logging.Options{Level: zapConfig.Level, Format: zapConfig.Format, EncodeLevel: zapConfig.EncodeLevel, Prefix: zapConfig.Prefix, StacktraceKey: zapConfig.StacktraceKey, LogInConsole: zapConfig.LogInConsole, ShowLine: zapConfig.ShowLine, RetentionDay: int(zapConfig.RetentionDay), FileOnlyModules: zapConfig.FileOnlyModules} if zapConfig.Director != "" { root = zapConfig.Director } return root, options } func httpAddress(server *conf.Server) string { if server != nil && server.Http != nil && server.Http.Addr != "" { return server.Http.Addr } return ":8000" } func swaggerAddress(server *conf.Server, admin *conf.AdminBackend) string { address := httpAddress(server) if strings.HasPrefix(address, ":") { address = "127.0.0.1" + address } else if host, port, err := net.SplitHostPort(address); err == nil && (host == "" || host == "0.0.0.0" || host == "::") { address = net.JoinHostPort("127.0.0.1", port) } prefix := "" if admin != nil { prefix = strings.Trim(admin.RouterPrefix, "/") } if prefix != "" { prefix = "/" + prefix } return "http://" + address + prefix + "/swagger/index.html" } func main() { flag.Parse() c := config.New( config.WithSource( file.NewSource(flagconf), ), ) defer c.Close() if err := c.Load(); err != nil { panic(err) } var bc conf.Bootstrap if err := c.Scan(&bc); err != nil { panic(err) } logRoot, logOptions := zapSettings(bc.Admin) loggerAttrs := []any{slog.String("service.id", id), slog.String("service.name", Name), slog.String("service.version", Version)} if bc.Admin != nil && bc.Admin.App != nil { loggerAttrs = append(loggerAttrs, slog.String("node", bc.Admin.App.Node), slog.String("app_id", bc.Admin.App.AppId), slog.String("env", bc.Admin.App.Env)) } logger, loggerControl := logging.NewReloadableZapLogger(logRoot, "application.log", logOptions, loggerAttrs...) defer loggerControl.Close() slog.SetDefault(logger) log.SetDefault(logger) if bc.Admin != nil { bc.Admin.ConfigPath = flagconf if info, err := os.Stat(flagconf); err == nil && info.IsDir() { bc.Admin.ConfigPath = filepath.Join(flagconf, "config.yaml") } } runtime := conf.NewRuntime(bc.Data, bc.Admin) unsubscribeLogger := runtime.Subscribe(func(_ *conf.Data, admin *conf.AdminBackend) { root, options := zapSettings(admin) loggerControl.Reload(root, options) }) defer unsubscribeLogger() app, cleanup, err := wireApp(bc.Server, runtime, logger, loggerControl, Version) if err != nil { panic(err) } defer cleanup() logger.Info("Kra administration service initialized", "mod", "system", "version", Version, "http_address", httpAddress(bc.Server), "swagger", swaggerAddress(bc.Server, bc.Admin), "admin_frontend", "http://127.0.0.1:8080") // start and wait for stop signal if err := app.Run(); err != nil { panic(err) } }