bootstrap.go 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. package agentmcp
  2. import (
  3. "context"
  4. "fmt"
  5. "os"
  6. "path/filepath"
  7. "github.com/spf13/viper"
  8. "go.uber.org/zap"
  9. "go.uber.org/zap/zapcore"
  10. "fwh-toolbox-server/cache"
  11. "fwh-toolbox-server/config"
  12. "fwh-toolbox-server/database"
  13. "fwh-toolbox-server/logger"
  14. "fwh-toolbox-server/repositories"
  15. )
  16. // Settings fwh-agent-mcp 自身的运行配置(AGENT_MCP_*),与主服务读取同一份 .env。
  17. type Settings struct {
  18. Token string // AGENT_MCP_TOKEN:Bearer 令牌,必填
  19. Listen string // AGENT_MCP_LISTEN:默认 127.0.0.1:8091
  20. MaxConcurrency int // AGENT_MCP_MAX_CONCURRENCY:默认 8
  21. LogPath string // AGENT_MCP_LOG_PATH:默认 /var/log/fwh-agent-mcp/agent-mcp.log
  22. }
  23. // LoadSettings 读取 AGENT_MCP_*:先读 .env / config.env(与 config.LoadConfig 相同的查找顺序),环境变量优先。
  24. func LoadSettings() Settings {
  25. v := viper.New()
  26. v.SetDefault("AGENT_MCP_LISTEN", "127.0.0.1:8091")
  27. v.SetDefault("AGENT_MCP_MAX_CONCURRENCY", 8)
  28. v.SetDefault("AGENT_MCP_LOG_PATH", "/var/log/fwh-agent-mcp/agent-mcp.log")
  29. for _, p := range []string{"./.env", "./config.env"} {
  30. v.SetConfigFile(p)
  31. v.SetConfigType("env")
  32. if err := v.ReadInConfig(); err == nil {
  33. break
  34. }
  35. }
  36. v.AutomaticEnv()
  37. return Settings{
  38. Token: v.GetString("AGENT_MCP_TOKEN"),
  39. Listen: v.GetString("AGENT_MCP_LISTEN"),
  40. MaxConcurrency: v.GetInt("AGENT_MCP_MAX_CONCURRENCY"),
  41. LogPath: v.GetString("AGENT_MCP_LOG_PATH"),
  42. }
  43. }
  44. // InitLogger 初始化日志:写入独立的日志文件;目录不可写时退回 stderr。
  45. // 同时设置 zap 全局 logger 与 logger 包变量(database 等模块直接引用 logger.Sugar)。
  46. func InitLogger(path string) *zap.Logger {
  47. outputs := []string{"stderr"}
  48. if path != "" {
  49. if err := os.MkdirAll(filepath.Dir(path), 0o755); err == nil {
  50. outputs = []string{path}
  51. } else {
  52. fmt.Fprintf(os.Stderr, "[fwh-agent-mcp] 创建日志目录失败,改为输出到 stderr: %v\n", err)
  53. }
  54. }
  55. cfg := zap.Config{
  56. Level: zap.NewAtomicLevelAt(zap.InfoLevel),
  57. Encoding: "json",
  58. EncoderConfig: zapcore.EncoderConfig{
  59. TimeKey: "ts",
  60. LevelKey: "level",
  61. NameKey: "logger",
  62. CallerKey: "caller",
  63. MessageKey: "msg",
  64. StacktraceKey: "stacktrace",
  65. LineEnding: zapcore.DefaultLineEnding,
  66. EncodeLevel: zapcore.LowercaseLevelEncoder,
  67. EncodeTime: zapcore.ISO8601TimeEncoder,
  68. EncodeDuration: zapcore.SecondsDurationEncoder,
  69. EncodeCaller: zapcore.ShortCallerEncoder,
  70. },
  71. OutputPaths: outputs,
  72. ErrorOutputPaths: outputs,
  73. }
  74. l, err := cfg.Build(zap.AddStacktrace(zap.ErrorLevel))
  75. if err != nil {
  76. fmt.Fprintf(os.Stderr, "[fwh-agent-mcp] logger 初始化失败: %v\n", err)
  77. l = zap.NewNop()
  78. }
  79. zap.ReplaceGlobals(l)
  80. logger.Log = l
  81. logger.Sugar = l.Sugar()
  82. logger.Std = zap.NewStdLog(l)
  83. return l
  84. }
  85. // InitInfra 加载主服务配置并初始化工具用到的基础设施:数据库、Redis、FWH 生产/测试 API、Plane。
  86. func InitInfra() error {
  87. cfg, err := config.LoadConfig()
  88. if err != nil {
  89. return fmt.Errorf("加载配置失败: %w", err)
  90. }
  91. if err := database.Init(&cfg.App); err != nil {
  92. return fmt.Errorf("数据库初始化失败: %w", err)
  93. }
  94. cache.Init(cfg.App.RedisAddr)
  95. if err := repositories.Init(context.Background()); err != nil {
  96. return fmt.Errorf("Repository 初始化失败: %w", err)
  97. }
  98. repositories.FwhHttpInit(&cfg.FwhApi)
  99. repositories.FwhTestHttpInit(&cfg.FwhTestApi)
  100. // 必须用 InitPlane(含 Feedback 专用实例 repositories.Plane),否则 feedback_create 会空指针
  101. repositories.InitPlane(&cfg.PlaneWebApi)
  102. return nil
  103. }