Skip to content

Commit f1ebc67

Browse files
vishrclaude
andcommitted
Enhance Logger Middleware Documentation with Detailed Configuration Examples
Addresses issue #2665 by providing comprehensive documentation for the Logger middleware including: **Configuration Examples:** - Basic usage with default settings - Custom simple and JSON formats - Custom time formatting - Header, query, form, and cookie logging - File output configuration - Custom tag functions - Conditional logging with Skipper - External logging service integration **Detailed Tag Reference:** - Complete list of all available tags (time, request, response, dynamic) - Clear explanations of each tag's purpose and format - Examples showing proper usage **Enhanced Field Documentation:** - Detailed descriptions for all LoggerConfig fields - Examples for each configuration option - Default values and behavior **Troubleshooting Section:** - Common issues and solutions - Performance optimization tips - Best practices for high-traffic applications **Function Documentation:** - Enhanced Logger() and LoggerWithConfig() documentation - Example outputs and usage patterns This makes the Logger middleware much more accessible to new users while providing advanced configuration guidance for experienced developers. Fixes #2665 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent b7a781c commit f1ebc67

File tree

1 file changed

+208
-41
lines changed

1 file changed

+208
-41
lines changed

middleware/logger.go

Lines changed: 208 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -18,55 +18,180 @@ import (
1818
)
1919

2020
// LoggerConfig defines the config for Logger middleware.
21+
//
22+
// # Configuration Examples
23+
//
24+
// ## Basic Usage with Default Settings
25+
//
26+
// e.Use(middleware.Logger())
27+
//
28+
// This uses the default JSON format that logs all common request/response details.
29+
//
30+
// ## Custom Simple Format
31+
//
32+
// e.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{
33+
// Format: "${time_rfc3339_nano} ${status} ${method} ${uri} ${latency_human}\n",
34+
// }))
35+
//
36+
// ## JSON Format with Custom Fields
37+
//
38+
// e.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{
39+
// Format: `{"timestamp":"${time_rfc3339_nano}","level":"info","remote_ip":"${remote_ip}",` +
40+
// `"method":"${method}","uri":"${uri}","status":${status},"latency":"${latency_human}",` +
41+
// `"user_agent":"${user_agent}","error":"${error}"}` + "\n",
42+
// }))
43+
//
44+
// ## Custom Time Format
45+
//
46+
// e.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{
47+
// Format: "${time_custom} ${method} ${uri} ${status}\n",
48+
// CustomTimeFormat: "2006-01-02 15:04:05",
49+
// }))
50+
//
51+
// ## Logging Headers and Parameters
52+
//
53+
// e.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{
54+
// Format: `{"time":"${time_rfc3339_nano}","method":"${method}","uri":"${uri}",` +
55+
// `"status":${status},"auth":"${header:Authorization}","user":"${query:user}",` +
56+
// `"form_data":"${form:action}","session":"${cookie:session_id}"}` + "\n",
57+
// }))
58+
//
59+
// ## Custom Output (File Logging)
60+
//
61+
// file, err := os.OpenFile("app.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
62+
// if err != nil {
63+
// log.Fatal(err)
64+
// }
65+
// defer file.Close()
66+
//
67+
// e.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{
68+
// Output: file,
69+
// }))
70+
//
71+
// ## Custom Tag Function
72+
//
73+
// e.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{
74+
// Format: `{"time":"${time_rfc3339_nano}","user_id":"${custom}","method":"${method}"}` + "\n",
75+
// CustomTagFunc: func(c echo.Context, buf *bytes.Buffer) (int, error) {
76+
// userID := getUserIDFromContext(c) // Your custom logic
77+
// return buf.WriteString(strconv.Itoa(userID))
78+
// },
79+
// }))
80+
//
81+
// ## Conditional Logging (Skip Certain Requests)
82+
//
83+
// e.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{
84+
// Skipper: func(c echo.Context) bool {
85+
// // Skip logging for health check endpoints
86+
// return c.Request().URL.Path == "/health" || c.Request().URL.Path == "/metrics"
87+
// },
88+
// }))
89+
//
90+
// ## Integration with External Logging Service
91+
//
92+
// logBuffer := &SyncBuffer{} // Thread-safe buffer for external service
93+
//
94+
// e.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{
95+
// Format: `{"timestamp":"${time_rfc3339_nano}","service":"my-api","level":"info",` +
96+
// `"method":"${method}","uri":"${uri}","status":${status},"latency_ms":${latency},` +
97+
// `"remote_ip":"${remote_ip}","user_agent":"${user_agent}","error":"${error}"}` + "\n",
98+
// Output: logBuffer,
99+
// }))
100+
//
101+
// # Available Tags
102+
//
103+
// ## Time Tags
104+
// - time_unix: Unix timestamp (seconds)
105+
// - time_unix_milli: Unix timestamp (milliseconds)
106+
// - time_unix_micro: Unix timestamp (microseconds)
107+
// - time_unix_nano: Unix timestamp (nanoseconds)
108+
// - time_rfc3339: RFC3339 format (2006-01-02T15:04:05Z07:00)
109+
// - time_rfc3339_nano: RFC3339 with nanoseconds
110+
// - time_custom: Uses CustomTimeFormat field
111+
//
112+
// ## Request Information
113+
// - id: Request ID from X-Request-ID header
114+
// - remote_ip: Client IP address (respects proxy headers)
115+
// - uri: Full request URI with query parameters
116+
// - host: Host header value
117+
// - method: HTTP method (GET, POST, etc.)
118+
// - path: URL path without query parameters
119+
// - route: Echo route pattern (e.g., /users/:id)
120+
// - protocol: HTTP protocol version
121+
// - referer: Referer header value
122+
// - user_agent: User-Agent header value
123+
//
124+
// ## Response Information
125+
// - status: HTTP status code
126+
// - error: Error message if request failed
127+
// - latency: Request processing time in nanoseconds
128+
// - latency_human: Human-readable processing time
129+
// - bytes_in: Request body size in bytes
130+
// - bytes_out: Response body size in bytes
131+
//
132+
// ## Dynamic Tags
133+
// - header:<NAME>: Value of specific header (e.g., header:Authorization)
134+
// - query:<NAME>: Value of specific query parameter (e.g., query:user_id)
135+
// - form:<NAME>: Value of specific form field (e.g., form:username)
136+
// - cookie:<NAME>: Value of specific cookie (e.g., cookie:session_id)
137+
// - custom: Output from CustomTagFunc
138+
//
139+
// # Troubleshooting
140+
//
141+
// ## Common Issues
142+
//
143+
// 1. **Missing logs**: Check if Skipper function is filtering out requests
144+
// 2. **Invalid JSON**: Ensure CustomTagFunc outputs valid JSON content
145+
// 3. **Performance issues**: Consider using a buffered writer for high-traffic applications
146+
// 4. **File permission errors**: Ensure write permissions when logging to files
147+
//
148+
// ## Performance Tips
149+
//
150+
// - Use time_unix formats for better performance than time_rfc3339
151+
// - Minimize the number of dynamic tags (header:, query:, form:, cookie:)
152+
// - Use Skipper to exclude high-frequency, low-value requests (health checks, etc.)
153+
// - Consider async logging for very high-traffic applications
21154
type LoggerConfig struct {
22155
// Skipper defines a function to skip middleware.
156+
// Use this to exclude certain requests from logging (e.g., health checks).
157+
//
158+
// Example:
159+
// Skipper: func(c echo.Context) bool {
160+
// return c.Request().URL.Path == "/health"
161+
// },
23162
Skipper Skipper
24163

25-
// Tags to construct the logger format.
26-
//
27-
// - time_unix
28-
// - time_unix_milli
29-
// - time_unix_micro
30-
// - time_unix_nano
31-
// - time_rfc3339
32-
// - time_rfc3339_nano
33-
// - time_custom
34-
// - id (Request ID)
35-
// - remote_ip
36-
// - uri
37-
// - host
38-
// - method
39-
// - path
40-
// - route
41-
// - protocol
42-
// - referer
43-
// - user_agent
44-
// - status
45-
// - error
46-
// - latency (In nanoseconds)
47-
// - latency_human (Human readable)
48-
// - bytes_in (Bytes received)
49-
// - bytes_out (Bytes sent)
50-
// - header:<NAME>
51-
// - query:<NAME>
52-
// - form:<NAME>
53-
// - custom (see CustomTagFunc field)
54-
//
55-
// Example "${remote_ip} ${status}"
164+
// Format defines the logging format using template tags.
165+
// Tags are enclosed in ${} and replaced with actual values.
166+
// See the detailed tag documentation above for all available options.
56167
//
57-
// Optional. Default value DefaultLoggerConfig.Format.
168+
// Default: JSON format with common fields
169+
// Example: "${time_rfc3339_nano} ${status} ${method} ${uri} ${latency_human}\n"
58170
Format string `yaml:"format"`
59171

60-
// Optional. Default value DefaultLoggerConfig.CustomTimeFormat.
172+
// CustomTimeFormat specifies the time format used by ${time_custom} tag.
173+
// Uses Go's reference time: Mon Jan 2 15:04:05 MST 2006
174+
//
175+
// Default: "2006-01-02 15:04:05.00000"
176+
// Example: "2006-01-02 15:04:05" or "15:04:05.000"
61177
CustomTimeFormat string `yaml:"custom_time_format"`
62178

63-
// CustomTagFunc is function called for `${custom}` tag to output user implemented text by writing it to buf.
64-
// Make sure that outputted text creates valid JSON string with other logged tags.
65-
// Optional.
179+
// CustomTagFunc is called when ${custom} tag is encountered.
180+
// Use this to add application-specific information to logs.
181+
// The function should write valid content for your log format.
182+
//
183+
// Example:
184+
// CustomTagFunc: func(c echo.Context, buf *bytes.Buffer) (int, error) {
185+
// userID := getUserFromContext(c)
186+
// return buf.WriteString(`"user_id":"` + userID + `"`)
187+
// },
66188
CustomTagFunc func(c echo.Context, buf *bytes.Buffer) (int, error)
67189

68-
// Output is a writer where logs in JSON format are written.
69-
// Optional. Default value os.Stdout.
190+
// Output specifies where logs are written.
191+
// Can be any io.Writer: files, buffers, network connections, etc.
192+
//
193+
// Default: os.Stdout
194+
// Example: Custom file, syslog, or external logging service
70195
Output io.Writer
71196

72197
template *fasttemplate.Template
@@ -85,13 +210,55 @@ var DefaultLoggerConfig = LoggerConfig{
85210
colorer: color.New(),
86211
}
87212

88-
// Logger returns a middleware that logs HTTP requests.
213+
// Logger returns a middleware that logs HTTP requests using the default configuration.
214+
//
215+
// The default format logs requests as JSON with the following fields:
216+
// - time: RFC3339 nano timestamp
217+
// - id: Request ID from X-Request-ID header
218+
// - remote_ip: Client IP address
219+
// - host: Host header
220+
// - method: HTTP method
221+
// - uri: Request URI
222+
// - user_agent: User-Agent header
223+
// - status: HTTP status code
224+
// - error: Error message (if any)
225+
// - latency: Processing time in nanoseconds
226+
// - latency_human: Human-readable processing time
227+
// - bytes_in: Request body size
228+
// - bytes_out: Response body size
229+
//
230+
// Example output:
231+
//
232+
// {"time":"2023-01-15T10:30:45.123456789Z","id":"","remote_ip":"127.0.0.1",
233+
// "host":"localhost:8080","method":"GET","uri":"/users/123","user_agent":"curl/7.81.0",
234+
// "status":200,"error":"","latency":1234567,"latency_human":"1.234567ms",
235+
// "bytes_in":0,"bytes_out":42}
236+
//
237+
// For custom configurations, use LoggerWithConfig instead.
89238
func Logger() echo.MiddlewareFunc {
90239
return LoggerWithConfig(DefaultLoggerConfig)
91240
}
92241

93-
// LoggerWithConfig returns a Logger middleware with config.
94-
// See: `Logger()`.
242+
// LoggerWithConfig returns a Logger middleware with custom configuration.
243+
//
244+
// This function allows you to customize all aspects of request logging including:
245+
// - Log format and fields
246+
// - Output destination
247+
// - Time formatting
248+
// - Custom tags and logic
249+
// - Request filtering
250+
//
251+
// See LoggerConfig documentation for detailed configuration examples and options.
252+
//
253+
// Example:
254+
//
255+
// e.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{
256+
// Format: "${time_rfc3339} ${status} ${method} ${uri} ${latency_human}\n",
257+
// Output: customLogWriter,
258+
// Skipper: func(c echo.Context) bool {
259+
// return c.Request().URL.Path == "/health"
260+
// },
261+
// }))
95262
func LoggerWithConfig(config LoggerConfig) echo.MiddlewareFunc {
96263
// Defaults
97264
if config.Skipper == nil {

0 commit comments

Comments
 (0)