package main import ( "context" "flag" "log/slog" "net" "os" "strings" "kra/internal/config" "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/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() ) // Service identity reported to logs, health output, and the API documentation. // A plain `go run` leaves the ldflags empty, so these are the fallbacks. const ( appName = "Kra Admin" appDescription = "Kratos 管理后台服务" defaultVersion = "v0.0.0-dev" ) func init() { flag.StringVar(&flagconf, "conf", "./configs", "config path, eg: -conf config.yaml") } // taskRegistry builds the process-wide registry from the dependency-free task // methods the static module catalog declares. Methods whose handlers need // constructed usecases are added later by buildRuntime. func taskRegistry() *platformtask.Registry { return platformtask.NewRegistry() } // buildRuntime is the binary-level list of modules with constructed route or // task dependencies: it activates their task methods and composes their routes. // Adding another runtime module is explicit here. func buildRuntime(registry *platformtask.Registry, systemRoutes *router.Routes, systemTasks *worker.TaskMethods) module.RouteRegistrar { systemTasks.RegisterTasks(registry) return systemRoutes } 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{"description": appDescription}), kratos.Logger(logger), kratos.Server( hs, scheduler, ), ) } func zapSettings(admin *config.Admin) (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 *config.Server) string { if server != nil && server.HTTP != nil && server.HTTP.Addr != "" { return server.HTTP.Addr } return ":8000" } func swaggerAddress(server *config.Server, admin *config.Admin) 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() // A plain `go run` leaves the ldflags empty, so fall back to the declared // identity rather than reporting a blank service. if Name == "" { Name = appName } if Version == "" { Version = defaultVersion } runtime, err := config.LoadStore(flagconf) if err != nil { panic(err) } defer runtime.Close() bc := runtime.Snapshot() if bc == nil { bc = &config.Config{} } 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) unsubscribeLogger := runtime.Subscribe(func(value *config.Config) { var admin *config.Admin if value != nil { admin = value.Admin } root, options := zapSettings(admin) loggerControl.Reload(root, options) }) defer unsubscribeLogger() application, 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 := application.Run(); err != nil { panic(err) } }