Skip to content

Commit 008b8d9

Browse files
committed
feat: feat: Add logger configuration.
1 parent 2cd2d8d commit 008b8d9

10 files changed

Lines changed: 209 additions & 21 deletions

File tree

.env.example

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,27 @@
1+
# Logger configuration for apiserver
2+
APISERVER_LOGGER_LEVEL=info
3+
APISERVER_LOGGER_FORMAT=json
4+
APISERVER_LOGGER_OUTPUT=stdout
5+
APISERVER_LOGGER_FILE_PATH=/var/log/mcp-gateway/apiserver.log
6+
APISERVER_LOGGER_MAX_SIZE=100
7+
APISERVER_LOGGER_MAX_BACKUPS=3
8+
APISERVER_LOGGER_MAX_AGE=7
9+
APISERVER_LOGGER_COMPRESS=true
10+
APISERVER_LOGGER_COLOR=false
11+
APISERVER_LOGGER_STACKTRACE=true
12+
13+
# Logger configuration for mcp-gateway
14+
LOGGER_LEVEL=info
15+
LOGGER_FORMAT=json
16+
LOGGER_OUTPUT=stdout
17+
LOGGER_FILE_PATH=/var/log/mcp-gateway/mcp-gateway.log
18+
LOGGER_MAX_SIZE=100
19+
LOGGER_MAX_BACKUPS=3
20+
LOGGER_MAX_AGE=7
21+
LOGGER_COMPRESS=true
22+
LOGGER_COLOR=false
23+
LOGGER_STACKTRACE=true
24+
125
# Database Configuration
226
APISERVER_DB_TYPE=sqlite
327
APISERVER_DB_HOST=localhost

cmd/apiserver/main.go

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package main
33
import (
44
"context"
55
"fmt"
6+
"github.com/mcp-ecosystem/mcp-gateway/pkg/logger"
67
"log"
78
"os"
89

@@ -47,22 +48,20 @@ func init() {
4748
}
4849

4950
// initLogger initializes the application logger
50-
func initLogger() *zap.Logger {
51-
logger, err := zap.NewProduction()
51+
func initLogger(cfg *config.APIServerConfig) *zap.Logger {
52+
logger, err := logger.NewLogger(&cfg.Logger)
5253
if err != nil {
5354
log.Fatalf("Failed to create logger: %v", err)
5455
}
5556
return logger
5657
}
5758

5859
// initConfig loads and returns the application configuration
59-
func initConfig(logger *zap.Logger) *config.APIServerConfig {
60-
cfg, cfgPath, err := config.LoadConfig[config.APIServerConfig](configPath)
60+
func initConfig() *config.APIServerConfig {
61+
cfg, _, err := config.LoadConfig[config.APIServerConfig](configPath)
6162
if err != nil {
62-
logger.Fatal("Failed to load configuration",
63-
zap.String("path", cfgPath), zap.Error(err))
63+
log.Fatalf("Failed to load configuration: %v", err)
6464
}
65-
logger.Info("Loaded configuration", zap.String("path", cfgPath))
6665
return cfg
6766
}
6867

@@ -148,12 +147,14 @@ func run() {
148147
ctx, cancel := context.WithCancel(context.Background())
149148
defer cancel()
150149

151-
// Initialize logger
152-
logger := initLogger()
150+
// Load configuration first
151+
cfg := initConfig()
152+
153+
// Initialize logger with configuration
154+
logger := initLogger(cfg)
153155
defer logger.Sync()
154156

155-
// Load configuration
156-
cfg := initConfig(logger)
157+
logger.Info("Starting apiserver", zap.String("version", version.Get()))
157158

158159
// Initialize services
159160
ntf := initNotifier(ctx, logger, &cfg.Notifier)
@@ -162,8 +163,6 @@ func run() {
162163
defer db.Close()
163164
store := initStore(logger, &cfg.Storage)
164165

165-
logger.Info("Starting apiserver", zap.String("version", version.Get()))
166-
167166
// Initialize router and start server
168167
router := initRouter(db, store, ntf, openaiClient)
169168
startServer(logger, router)

cmd/mcp-gateway/main.go

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"errors"
66
"fmt"
7+
"github.com/mcp-ecosystem/mcp-gateway/pkg/logger"
78
"net/http"
89
"os"
910
"os/signal"
@@ -75,7 +76,7 @@ var (
7576
}
7677

7778
// Initialize logger
78-
logger, err := zap.NewProduction()
79+
logger, err := logger.NewLogger(&cfg.Logger)
7980
if err != nil {
8081
fmt.Printf("Failed to initialize logger: %v\n", err)
8182
os.Exit(1)
@@ -205,18 +206,19 @@ func handleReload(ctx context.Context, logger *zap.Logger, store storage.Store,
205206
func run() {
206207
ctx, cancel := context.WithCancel(context.Background())
207208

208-
logger, err := zap.NewProduction()
209+
// Load configuration first
210+
cfg, cfgPath, err := config.LoadConfig[config.MCPGatewayConfig](cnst.MCPGatewayYaml)
209211
if err != nil {
210-
panic(err)
212+
panic(fmt.Sprintf("Failed to load service configuration: %v", err))
211213
}
212-
defer logger.Sync()
213214

214-
cfg, cfgPath, err := config.LoadConfig[config.MCPGatewayConfig](cnst.MCPGatewayYaml)
215+
// Initialize logger with configuration
216+
logger, err := logger.NewLogger(&cfg.Logger)
215217
if err != nil {
216-
logger.Fatal("Failed to load service configuration",
217-
zap.String("path", cfgPath),
218-
zap.Error(err))
218+
panic(fmt.Sprintf("Failed to initialize logger: %v", err))
219219
}
220+
defer logger.Sync()
221+
220222
logger.Info("Loaded configuration", zap.String("path", cfgPath))
221223

222224
// Initialize PID manager

configs/apiserver.yaml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,16 @@
1+
# Logger configuration
2+
logger:
3+
level: "${APISERVER_LOGGER_LEVEL:info}" # debug, info, warn, error
4+
format: "${APISERVER_LOGGER_FORMAT:json}" # json, console
5+
output: "${APISERVER_LOGGER_OUTPUT:stdout}" # stdout, file
6+
file_path: "${APISERVER_LOGGER_FILE_PATH:/var/log/mcp-gateway/apiserver.log}" # path to log file when output is file
7+
max_size: ${APISERVER_LOGGER_MAX_SIZE:100} # max size of log file in MB
8+
max_backups: ${APISERVER_LOGGER_MAX_BACKUPS:3} # max number of backup files
9+
max_age: ${APISERVER_LOGGER_MAX_AGE:7} # max age of backup files in days
10+
compress: ${APISERVER_LOGGER_COMPRESS:true} # whether to compress backup files
11+
color: ${APISERVER_LOGGER_COLOR:false} # whether to use color in console output
12+
stacktrace: ${APISERVER_LOGGER_STACKTRACE:true} # whether to include stacktrace in error logs
13+
114
database:
215
type: "${APISERVER_DB_TYPE:sqlite}"
316
host: "${APISERVER_DB_HOST:localhost}"

configs/mcp-gateway.yaml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,19 @@
11
port: ${MCP_GATEWAY_PORT:5235}
22
pid: "${MCP_GATEWAY_PID:/var/run/mcp-gateway.pid}"
33

4+
# Logger configuration
5+
logger:
6+
level: "${LOGGER_LEVEL:info}" # debug, info, warn, error
7+
format: "${LOGGER_FORMAT:json}" # json, console
8+
output: "${LOGGER_OUTPUT:stdout}" # stdout, file
9+
file_path: "${LOGGER_FILE_PATH:/var/log/mcp-gateway/mcp-gateway.log}" # path to log file when output is file
10+
max_size: ${LOGGER_MAX_SIZE:100} # max size of log file in MB
11+
max_backups: ${LOGGER_MAX_BACKUPS:3} # max number of backup files
12+
max_age: ${LOGGER_MAX_AGE:7} # max age of backup files in days
13+
compress: ${LOGGER_COMPRESS:true} # whether to compress backup files
14+
color: ${LOGGER_COLOR:false} # whether to use color in console output
15+
stacktrace: ${LOGGER_STACKTRACE:true} # whether to include stacktrace in error logs
16+
417
# Storage configuration
518
storage:
619
type: "${GATEWAY_STORAGE_TYPE:db}" # disk or db

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ require (
1414
github.com/spf13/cobra v1.9.1
1515
github.com/stretchr/testify v1.10.0
1616
go.uber.org/zap v1.27.0
17+
gopkg.in/natefinch/lumberjack.v2 v2.2.1
1718
gopkg.in/yaml.v3 v3.0.1
1819
gorm.io/driver/mysql v1.5.7
1920
gorm.io/driver/postgres v1.5.7

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,8 @@ google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/
178178
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
179179
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
180180
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
181+
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
182+
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
181183
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
182184
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
183185
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=

internal/common/config/apiserver.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ type (
88
OpenAI OpenAIConfig `yaml:"openai"`
99
Storage StorageConfig `yaml:"storage"`
1010
Notifier NotifierConfig `yaml:"notifier"`
11+
Logger LoggerConfig `yaml:"logger"`
1112
}
1213

1314
DatabaseConfig struct {

internal/common/config/config.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ type (
1818
Storage StorageConfig `yaml:"storage"`
1919
Notifier NotifierConfig `yaml:"notifier"`
2020
Session SessionConfig `yaml:"session"`
21+
Logger LoggerConfig `yaml:"logger"`
2122
}
2223

2324
// SessionConfig represents the session storage configuration
@@ -33,6 +34,20 @@ type (
3334
DB int `yaml:"db"`
3435
Topic string `yaml:"topic"`
3536
}
37+
38+
// LoggerConfig represents the logger configuration
39+
LoggerConfig struct {
40+
Level string `yaml:"level"` // debug, info, warn, error
41+
Format string `yaml:"format"` // json, console
42+
Output string `yaml:"output"` // stdout, file
43+
FilePath string `yaml:"file_path"` // path to log file when output is file
44+
MaxSize int `yaml:"max_size"` // max size of log file in MB
45+
MaxBackups int `yaml:"max_backups"` // max number of backup files
46+
MaxAge int `yaml:"max_age"` // max age of backup files in days
47+
Compress bool `yaml:"compress"` // whether to compress backup files
48+
Color bool `yaml:"color"` // whether to use color in console output
49+
Stacktrace bool `yaml:"stacktrace"` // whether to include stacktrace in error logs
50+
}
3651
)
3752

3853
type Type interface {

pkg/logger/logger.go

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
package logger
2+
3+
import (
4+
"os"
5+
"path/filepath"
6+
7+
"github.com/mcp-ecosystem/mcp-gateway/internal/common/config"
8+
"go.uber.org/zap"
9+
"go.uber.org/zap/zapcore"
10+
"gopkg.in/natefinch/lumberjack.v2"
11+
)
12+
13+
// NewLogger creates a new logger based on configuration
14+
func NewLogger(cfg *config.LoggerConfig) (*zap.Logger, error) {
15+
// Set default values if not specified
16+
if cfg.Level == "" {
17+
cfg.Level = "info"
18+
}
19+
if cfg.Format == "" {
20+
cfg.Format = "json"
21+
}
22+
if cfg.Output == "" {
23+
cfg.Output = "stdout"
24+
}
25+
if cfg.MaxSize == 0 {
26+
cfg.MaxSize = 100 // 100MB
27+
}
28+
if cfg.MaxBackups == 0 {
29+
cfg.MaxBackups = 3
30+
}
31+
if cfg.MaxAge == 0 {
32+
cfg.MaxAge = 7 // 7 days
33+
}
34+
35+
// Create encoder config
36+
encoderConfig := zapcore.EncoderConfig{
37+
TimeKey: "time",
38+
LevelKey: "level",
39+
NameKey: "logger",
40+
CallerKey: "caller",
41+
MessageKey: "msg",
42+
StacktraceKey: "stacktrace",
43+
LineEnding: zapcore.DefaultLineEnding,
44+
EncodeLevel: zapcore.LowercaseLevelEncoder,
45+
EncodeTime: zapcore.ISO8601TimeEncoder,
46+
EncodeDuration: zapcore.SecondsDurationEncoder,
47+
EncodeCaller: zapcore.ShortCallerEncoder,
48+
}
49+
50+
// Set color if enabled
51+
if cfg.Color && cfg.Format == "console" {
52+
encoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder
53+
}
54+
55+
// Create encoder
56+
var encoder zapcore.Encoder
57+
if cfg.Format == "json" {
58+
encoder = zapcore.NewJSONEncoder(encoderConfig)
59+
} else {
60+
encoder = zapcore.NewConsoleEncoder(encoderConfig)
61+
}
62+
63+
// Create core
64+
var core zapcore.Core
65+
if cfg.Output == "file" {
66+
// Ensure log directory exists
67+
if err := os.MkdirAll(filepath.Dir(cfg.FilePath), 0755); err != nil {
68+
return nil, err
69+
}
70+
71+
// Create lumberjack logger for file rotation
72+
writer := &lumberjack.Logger{
73+
Filename: cfg.FilePath,
74+
MaxSize: cfg.MaxSize,
75+
MaxBackups: cfg.MaxBackups,
76+
MaxAge: cfg.MaxAge,
77+
Compress: cfg.Compress,
78+
}
79+
80+
core = zapcore.NewCore(
81+
encoder,
82+
zapcore.AddSync(writer),
83+
getLogLevel(cfg.Level),
84+
)
85+
} else {
86+
core = zapcore.NewCore(
87+
encoder,
88+
zapcore.AddSync(os.Stdout),
89+
getLogLevel(cfg.Level),
90+
)
91+
}
92+
93+
// Create logger
94+
logger := zap.New(core, zap.AddCaller(), zap.AddCallerSkip(1))
95+
96+
// Add stacktrace if enabled
97+
if cfg.Stacktrace {
98+
logger = logger.WithOptions(zap.AddStacktrace(zapcore.ErrorLevel))
99+
}
100+
101+
return logger, nil
102+
}
103+
104+
// getLogLevel converts string level to zapcore.Level
105+
func getLogLevel(level string) zapcore.Level {
106+
switch level {
107+
case "debug":
108+
return zapcore.DebugLevel
109+
case "info":
110+
return zapcore.InfoLevel
111+
case "warn":
112+
return zapcore.WarnLevel
113+
case "error":
114+
return zapcore.ErrorLevel
115+
default:
116+
return zapcore.InfoLevel
117+
}
118+
}

0 commit comments

Comments
 (0)