Skip to content

Commit 687146e

Browse files
logging improvements (#20)
* enhancement: Added request id logging, added logging for 4xx errors since they weren't caught by route-specific logging * enhancement: Added the ability to dump request content for debugging * Update internal/log/ecs.go Co-authored-by: David Cheung <[email protected]> Co-authored-by: David Cheung <[email protected]>
1 parent 9f5edeb commit 687146e

File tree

4 files changed

+35
-2
lines changed

4 files changed

+35
-2
lines changed

cmd/server/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ func main() {
5050
NotificationApiService := service.NewNotificationApiService(config)
5151
NotificationApiController := server.NewNotificationApiController(NotificationApiService)
5252

53-
router := server.NewRouter(EmailApiController, HealthApiController, NotificationApiController)
53+
router := server.Logger(server.NewRouter(EmailApiController, HealthApiController, NotificationApiController), "")
5454

5555
serverAddress := fmt.Sprintf("0.0.0.0:%d", config.Port)
5656
server := &http.Server{Addr: serverAddress, Handler: router}

internal/config/config.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ type Config struct {
1313
SlackAPIKey string
1414
GracefulShutdownTimeout time.Duration
1515
StructuredLogging bool
16+
DebugDumpRequests bool
1617
}
1718

1819
var config *Config
@@ -24,6 +25,7 @@ const (
2425
SlackAPIKey
2526
GracefulShutdownTimeout
2627
StructuredLogging
28+
DebugDumpRequests
2729
)
2830

2931
// GetConfig returns a pointer to the singleton Config object
@@ -50,12 +52,16 @@ func loadConfig() *Config {
5052
viper.SetDefault(StructuredLogging, "false")
5153
viper.BindEnv(StructuredLogging, "STRUCTURED_LOGGING")
5254

55+
viper.SetDefault(DebugDumpRequests, "false")
56+
viper.BindEnv(DebugDumpRequests, "DEBUG_DUMP_REQUESTS")
57+
5358
config := Config{
5459
Port: viper.GetInt(Port),
5560
SendgridAPIKey: viper.GetString(SendgridAPIKey),
5661
SlackAPIKey: viper.GetString(SlackAPIKey),
5762
GracefulShutdownTimeout: viper.GetDuration(GracefulShutdownTimeout),
5863
StructuredLogging: viper.GetBool(StructuredLogging),
64+
DebugDumpRequests: viper.GetBool(DebugDumpRequests),
5965
}
6066

6167
return &config

internal/log/ecs.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,13 @@ func (o ECSEvent) MarshalLogObject(enc zapcore.ObjectEncoder) error {
6060
enc.AddInt64("duration", int64(o.Duration))
6161
return nil
6262
}
63+
64+
// ECSTrace represents a subset of the fields of the ECS Trace object
65+
type ECSTrace struct {
66+
ID string
67+
}
68+
69+
func (o ECSTrace) MarshalLogObject(enc zapcore.ObjectEncoder) error {
70+
enc.AddString("id", o.ID)
71+
return nil
72+
}

internal/server/logger.go

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ package server
1111

1212
import (
1313
"net/http"
14+
"net/http/httputil"
1415
"time"
1516

1617
"github.com/commitdev/zero-notification-service/internal/config"
@@ -33,9 +34,23 @@ func Logger(inner http.Handler, name string) http.Handler {
3334
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
3435
start := time.Now()
3536

37+
// If configured, dump the content of the request for debugging
38+
if config.GetConfig().DebugDumpRequests {
39+
requestDump, err := httputil.DumpRequest(r, true)
40+
if err != nil {
41+
zap.S().Errorw("Error getting request content", err)
42+
}
43+
zap.S().Debugw("HTTP Request Content", zap.ByteString("content", requestDump))
44+
}
45+
3646
lrw := &loggingResponseWriter{w, http.StatusOK}
3747
inner.ServeHTTP(lrw, r)
3848

49+
// Use the global handler for 4xx status codes, as there won't be a route-specific one
50+
if !(lrw.statusCode >= 400 && lrw.statusCode <= 499) && name == "" {
51+
return
52+
}
53+
3954
if config.GetConfig().StructuredLogging {
4055
// Don't log health checks in a cloud environment - name is defined in the schema
4156
if name != "ReadyCheck" {
@@ -49,8 +64,9 @@ func Logger(inner http.Handler, name string) http.Handler {
4964
}
5065
url := log.ECSURL{Original: r.RequestURI}
5166
event := log.ECSEvent{Action: name, Duration: time.Since(start)}
67+
trace := log.ECSTrace{ID: r.Header.Get("X-Request-ID")}
5268

53-
zap.S().Infow("HTTP Request", zap.Any("http", http), zap.Any("url", url), zap.Any("event", event))
69+
zap.S().Infow("HTTP Request", zap.Any("http", http), zap.Any("url", url), zap.Any("event", event), zap.Any("trace", trace))
5470
}
5571
} else {
5672
zap.S().Infow("HTTP Request",
@@ -59,6 +75,7 @@ func Logger(inner http.Handler, name string) http.Handler {
5975
"url", r.RequestURI,
6076
"action", name,
6177
"duration", time.Since(start),
78+
"request_id", r.Header.Get("X-Request-ID"),
6279
)
6380
}
6481
})

0 commit comments

Comments
 (0)