Use structured logging with libraries like zap for JSON-formatted, easily parsable logs. 2. Include contextual information such as request IDs, user IDs, and timestamps via contextual loggers in middleware. 3. Log at appropriate levels—fatal, error, warn, info, debug—enabling debug only when needed. 4. Avoid logging sensitive data like passwords or PII; sanitize inputs and redact sensitive fields. 5. Use correlation IDs to trace requests across distributed systems by injecting and propagating X-Correlation-ID headers. 6. Log errors only once at the handling layer to prevent duplication, wrapping errors with context instead. 7. Rotate log files using tools like lumberjack to manage disk usage by size or time. 8. Leverage Go’s context to carry and enrich loggers across request flows. Effective Go logging relies on structured, contextual, secure, and non-redundant logs with proper error handling and traceability, which saves critical debugging time in production.
Writing effective logs in Go applications is essential for debugging, monitoring, and maintaining software in production. While Go’s built-in log
package provides basic functionality, building a robust logging strategy requires more thoughtful design. Here are key practices to follow for effective logging in Go.

1. Use Structured Logging
Unstructured logs (plain text) are hard to parse and search at scale. Instead, use structured logging with key-value pairs so logs can be easily processed by tools like Loki, ELK, or Datadog.
Popular libraries like logrus or zap support JSON-formatted logs out of the box.

// Example with zap logger, _ := zap.NewProduction() defer logger.Sync() logger.Info("User login attempt", zap.String("user_id", "12345"), zap.Bool("success", false), zap.String("ip", "192.0.2.1"), )
This outputs structured JSON:
{ "level": "info", "msg": "User login attempt", "user_id": "12345", "success": false, "ip": "192.0.2.1", "ts": 1717734090.123 }
Tip: Prefer zap
in performance-critical services—its structured logging is significantly faster than logrus
.

2. Include Contextual Information
Logs without context are often useless. Always include relevant metadata such as:
- Request or trace IDs
- User IDs
- Endpoint or function name
- Timestamps (usually added automatically)
- Service or component name
One effective way is to attach a contextual logger to incoming requests (e.g., in HTTP middleware):
func loggingMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Create a request-scoped logger logger := baseLogger.With( zap.String("method", r.Method), zap.String("path", r.URL.Path), zap.String("remote_ip", r.RemoteAddr), ) // Attach logger to context ctx := context.WithValue(r.Context(), "logger", logger) next.ServeHTTP(w, r.WithContext(ctx)) }) }
Later in handlers:
logger := ctx.Value("logger").(*zap.Logger) logger.Info("Processing payment", zap.String("order_id", "ord-789"))
3. Log at the Right Level
Use appropriate log levels to filter noise and highlight important events. Standard levels (from most to least severe):
Fatal
– Application will exit; use sparinglyError
– Something went wrong (e.g., DB failure)Warn
– Unexpected but not breaking (e.g., deprecated API call)Info
– Key operations (e.g., service started, user logged in)Debug
– Detailed info for troubleshooting (disable in production)
Best practice:
Enable debug
logs only in development or when troubleshooting. In production, default to info
or warn
.
4. Avoid Logging Sensitive Data
Never log passwords, tokens, PII (personally identifiable information), or full request bodies.
// ? Dangerous logger.Info("Login request", zap.String("body", string(rawBody))) // ? Safe logger.Info("Login attempt", zap.String("user", username))
If you must log payloads, sanitize them first or use redaction:
func sanitizeBody(body string) string { // Remove known sensitive fields re := regexp.MustCompile(`"password":"[^"]*"`) return re.ReplaceAllString(body, `"password":"***"`) }
Also consider using environment-aware log filtering or masking in your logging pipeline.
5. Use Correlation IDs for Tracing
In distributed systems, a single user request may touch multiple services. Use a correlation ID (also called trace ID) to track the flow across services.
// Generate or extract correlation ID from headers corrID := r.Header.Get("X-Correlation-ID") if corrID == "" { corrID = uuid.New().String() } logger := baseLogger.With(zap.String("correlation_id", corrID))
Propagate this ID in outgoing requests:
req, _ := http.NewRequest("GET", "http://service-b/api", nil) req.Header.Set("X-Correlation-ID", corrID)
This makes debugging distributed flows much easier.
6. Don’t Log Errors and Return Them
A common mistake is logging an error at every layer it passes through, creating log spam.
? Log an error only once, at the point where it’s handled (e.g., final retry attempt failed, or returning 500 to client).
? Don’t do this:
if err != nil { log.Printf("DB error: %v", err) return err }
Instead:
if err != nil { return fmt.Errorf("failed to process order: %w", err) }
Handle and log at the top level (e.g., HTTP handler):
if err := processOrder(); err != nil { logger.Error("Request failed", zap.Error(err)) http.Error(w, "Internal error", 500) }
This avoids duplication and keeps logs clean.
7. Rotate and Manage Log Files (if writing to disk)
If you write logs to files (e.g., for standalone services), use log rotation to prevent disk exhaustion.
Use libraries like lumberjack to rotate logs based on size, time, or number of backups.
w := zapcore.AddSync(&lumberjack.Logger{ Filename: "/var/log/myapp.log", MaxSize: 100, // MB MaxBackups: 3, MaxAge: 7, // days }) core := zapcore.NewCore( zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()), w, zap.InfoLevel, ) logger := zap.New(core)
8. Consider Using context
for Distributed Logging
Go’s context
package is ideal for carrying request-scoped values, including loggers. You can enrich the logger at different layers:
ctx = context.WithValue(ctx, "logger", logger.With(zap.String("step", "validation")))
Though better alternatives exist (like using a middleware-based logger), this pattern helps in long-running or complex workflows.
Basically, good logging in Go comes down to: structured output, contextual data, proper levels, avoiding duplication, and security. Use zap
or logrus
, add correlation IDs, and never log secrets. It’s not flashy, but it saves hours when things go wrong.
The above is the detailed content of Effective Logging Practices in Go Applications. For more information, please follow other related articles on the PHP Chinese website!

Hot AI Tools

Undress AI Tool
Undress images for free

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Clothoff.io
AI clothes remover

Video Face Swap
Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Hot Tools

Notepad++7.3.1
Easy-to-use and free code editor

SublimeText3 Chinese version
Chinese version, very easy to use

Zend Studio 13.0.1
Powerful PHP integrated development environment

Dreamweaver CS6
Visual web development tools

SublimeText3 Mac version
God-level code editing software (SublimeText3)

Hot Topics

Go's template engine provides powerful dynamic content generation capabilities through text/template and html/template packages, where html/template has automatic escape function to prevent XSS attacks, so it should be used first when generating HTML. 1. Use {{}} syntax to insert variables, conditional judgments and loops, such as {{.FieldName}} to access structure fields, {{if}} and {{range}} to implement logical control. 2. The template supports Go data structures such as struct, slice and map, and the dot in the range represents the current iterative element. 3. The named template can be defined through define and reused with the template directive. 4.ht

Go and Kafka integration is an effective solution to build high-performance real-time data systems. The appropriate client library should be selected according to needs: 1. Priority is given to kafka-go to obtain simple Go-style APIs and good context support, suitable for rapid development; 2. Select Sarama when fine control or advanced functions are required; 3. When implementing producers, you need to configure the correct Broker address, theme and load balancing strategy, and manage timeouts and closings through context; 4. Consumers should use consumer groups to achieve scalability and fault tolerance, automatically submit offsets and use concurrent processing reasonably; 5. Use JSON, Avro or Protobuf for serialization, and it is recommended to combine SchemaRegistr

When passing slices in Go, it is usually passed directly by value, because the slice header contains a pointer to the underlying array, and copying the slice header will not copy the underlying data, so the modification of elements in the function will affect the original slice; 1. If you need to reassign or adjust the slice length within the function and make the change take effect, you should pass the slice pointer; 2. Otherwise, you can pass the slice directly without using a pointer; 3. If reallocation may be triggered when using append, you must pass through the pointer to make the updated slice visible to the outside. Therefore, unless the entire slice is to be replaced, the slice should be passed in the form of a value.

govetcatchescommonlogicalerrorsandsuspiciousconstructsinGocodesuchas1)misuseofprintf-stylefunctionswithincorrectarguments,2)unkeyedstructliteralsthatmayleadtoincorrectfieldassignments,3)sendingtoclosedchannelswhichcausespanics,4)ineffectiveassignment

Usereflect.ValueOfandreflect.TypeOftogetruntimevaluesandtypes;2.Inspecttypedetailswithreflect.TypemethodslikeName()andKind();3.Modifyvaluesviareflect.Value.Elem()andCanSet()afterpassingapointer;4.CallmethodsdynamicallyusingMethodByName()andCall();5.R

To embed the file contents into the string of the Go program, you should use go:embed (Go1.16) to embed the file at compile time; 1. Add the //go:embed directive above the target variable; 2. Ensure the file path is correct and the file exists; 3. Use string type variables to receive text content; 4. Build the project through gobuild to include the file content. This method is safe and efficient and does not require additional tools, and ultimately implements the file contents directly into the binary file as strings.

In Go language, HTTP middleware is implemented through functions, and its core answer is: the middleware is a function that receives and returns http.Handler, used to execute general logic before and after request processing. 1. The middleware function signature is like func (Middleware(nexthttp.Handler)http.Handler), which achieves functional expansion by wrapping the original processor; 2. The log middleware in the example records the request method, path, client address and processing time-consuming, which is convenient for monitoring and debugging; 3. The authentication middleware checks the Authorization header, and returns 401 or 403 errors when verification fails to ensure secure access; 4. Multiple middleware can be nested to adjust

The method to enable IIS logging is as follows: 1. Select the website or server node in the IIS manager, double-click the "Log" module, set the log format to W3C, specify the log directory and use local time to create files, if the status is stopped, click Start log; 2. Set the log scrolling frequency, recommend scrolling every day, and set to keep up to 7 files to save space; 3. For IISExpress or non-default sites, you need to check the configuration items in applicationHost.config to ensure that enabled is set to true; 4. After accessing the website, check whether the log directory generates a u_exYYYMMDD.log file, and confirm the IIS running status and directory writing permissions. The whole process requires
