Added HLog documentation and updated home page.
- Added comprehensive HLog wiki page with all features and examples - Added HLog entry to wiki home page 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
476
HLog.md
Normal file
476
HLog.md
Normal file
@@ -0,0 +1,476 @@
|
|||||||
|
# HLog - Structured Logging for Go
|
||||||
|
|
||||||
|
**Version:** v0.10.1-hlogdoc
|
||||||
|
|
||||||
|
HLog is a structured logging package built on top of [zerolog](https://github.com/rs/zerolog), providing simple configuration, flexible output options, and automatic log file management for Go applications.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go get git.haelnorr.com/h/golib/hlog
|
||||||
|
```
|
||||||
|
|
||||||
|
## Key Concepts and Features
|
||||||
|
|
||||||
|
HLog provides the following key features:
|
||||||
|
|
||||||
|
- **Multiple Output Modes**: Log to console, file, or both simultaneously
|
||||||
|
- **Configurable Log Levels**: Support for trace, debug, info, warn, error, fatal, and panic levels
|
||||||
|
- **Environment-Based Configuration**: Easy setup using environment variables
|
||||||
|
- **Programmatic Configuration**: Full control via Config struct when needed
|
||||||
|
- **Automatic File Management**: Handles log file creation and management
|
||||||
|
- **High Performance**: Built on zerolog for minimal overhead
|
||||||
|
- **Error Stack Traces**: Automatic stack trace marshaling for errors
|
||||||
|
- **Flexible File Modes**: Choose between append or overwrite modes
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
The simplest way to get started with HLog is using environment variables:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Set environment variables
|
||||||
|
export LOG_LEVEL=info
|
||||||
|
export LOG_OUTPUT=console
|
||||||
|
```
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"git.haelnorr.com/h/golib/hlog"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Load configuration from environment
|
||||||
|
cfg, err := hlog.ConfigFromEnv()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create logger
|
||||||
|
logger, err := hlog.NewLogger(cfg, os.Stdout)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
defer logger.CloseLogFile()
|
||||||
|
|
||||||
|
// Start logging
|
||||||
|
logger.Info().Msg("Application started")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
HLog can be configured in two ways: via environment variables or programmatically.
|
||||||
|
|
||||||
|
### Using ConfigFromEnv (Recommended)
|
||||||
|
|
||||||
|
The `ConfigFromEnv()` function reads configuration from environment variables:
|
||||||
|
|
||||||
|
```go
|
||||||
|
cfg, err := hlog.ConfigFromEnv()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
logger, err := hlog.NewLogger(cfg, os.Stdout)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
defer logger.CloseLogFile()
|
||||||
|
```
|
||||||
|
|
||||||
|
### Environment Variables
|
||||||
|
|
||||||
|
| Variable | Description | Valid Values | Default | Required |
|
||||||
|
|----------|-------------|--------------|---------|----------|
|
||||||
|
| `LOG_LEVEL` | Log level for filtering messages | trace, debug, info, warn, error, fatal, panic | info | No |
|
||||||
|
| `LOG_OUTPUT` | Output destination | console, file, both | console | No |
|
||||||
|
| `LOG_DIR` | Directory path for log files | Any valid directory path | - | Yes (when LOG_OUTPUT is "file" or "both") |
|
||||||
|
| `LOG_FILE_NAME` | Name of the log file | Any valid filename | - | Yes (when LOG_OUTPUT is "file" or "both") |
|
||||||
|
| `LOG_APPEND` | Append to existing log file | true, false | true | No |
|
||||||
|
|
||||||
|
### Programmatic Configuration
|
||||||
|
|
||||||
|
For more control, create a `Config` struct directly:
|
||||||
|
|
||||||
|
```go
|
||||||
|
cfg := &hlog.Config{
|
||||||
|
LogLevel: hlog.InfoLevel,
|
||||||
|
LogOutput: "both",
|
||||||
|
LogDir: "/var/log/myapp",
|
||||||
|
LogFileName: "application.log",
|
||||||
|
LogAppend: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
logger, err := hlog.NewLogger(cfg, os.Stdout)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
defer logger.CloseLogFile()
|
||||||
|
```
|
||||||
|
|
||||||
|
## Log Levels
|
||||||
|
|
||||||
|
HLog supports seven log levels, from most to least verbose:
|
||||||
|
|
||||||
|
| Level | Description | Use Case |
|
||||||
|
|-------|-------------|----------|
|
||||||
|
| `trace` | Most verbose | Very detailed debugging, function entry/exit |
|
||||||
|
| `debug` | Debug information | Detailed debugging during development |
|
||||||
|
| `info` | Informational | General application events |
|
||||||
|
| `warn` | Warnings | Potentially harmful situations |
|
||||||
|
| `error` | Errors | Error events that might still allow the app to continue |
|
||||||
|
| `fatal` | Fatal errors | Severe errors that will cause the application to exit |
|
||||||
|
| `panic` | Panic | Panic-level errors that will panic the application |
|
||||||
|
|
||||||
|
### Setting Log Levels
|
||||||
|
|
||||||
|
Via environment:
|
||||||
|
```bash
|
||||||
|
export LOG_LEVEL=debug
|
||||||
|
```
|
||||||
|
|
||||||
|
Programmatically:
|
||||||
|
```go
|
||||||
|
cfg := &hlog.Config{
|
||||||
|
LogLevel: hlog.DebugLevel,
|
||||||
|
// ... other config
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Logging at Different Levels
|
||||||
|
|
||||||
|
```go
|
||||||
|
logger.Trace().Msg("Entering function")
|
||||||
|
logger.Debug().Str("user_id", "123").Msg("User details loaded")
|
||||||
|
logger.Info().Msg("Server started on port 8080")
|
||||||
|
logger.Warn().Msg("Cache miss, fetching from database")
|
||||||
|
logger.Error().Err(err).Msg("Failed to connect to database")
|
||||||
|
logger.Fatal().Msg("Cannot start: configuration file missing")
|
||||||
|
logger.Panic().Msg("Unrecoverable error occurred")
|
||||||
|
```
|
||||||
|
|
||||||
|
## Output Modes
|
||||||
|
|
||||||
|
HLog supports three output modes:
|
||||||
|
|
||||||
|
### Console Output
|
||||||
|
|
||||||
|
Logs are written to the provided `io.Writer` (typically `os.Stdout` or `os.Stderr`) with human-readable formatting:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export LOG_OUTPUT=console
|
||||||
|
```
|
||||||
|
|
||||||
|
```go
|
||||||
|
logger, err := hlog.NewLogger(cfg, os.Stdout)
|
||||||
|
```
|
||||||
|
|
||||||
|
### File Output
|
||||||
|
|
||||||
|
Logs are written to a file in the specified directory:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export LOG_OUTPUT=file
|
||||||
|
export LOG_DIR=/var/log/myapp
|
||||||
|
export LOG_FILE_NAME=server.log
|
||||||
|
```
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Note: io.Writer can be nil when using file-only output
|
||||||
|
logger, err := hlog.NewLogger(cfg, nil)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Both (Console + File)
|
||||||
|
|
||||||
|
Logs are written to both console and file simultaneously:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export LOG_OUTPUT=both
|
||||||
|
export LOG_DIR=/var/log/myapp
|
||||||
|
export LOG_FILE_NAME=server.log
|
||||||
|
```
|
||||||
|
|
||||||
|
```go
|
||||||
|
logger, err := hlog.NewLogger(cfg, os.Stdout)
|
||||||
|
```
|
||||||
|
|
||||||
|
## File Management
|
||||||
|
|
||||||
|
### Append Mode
|
||||||
|
|
||||||
|
By default, HLog appends to existing log files, preserving logs across application restarts:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export LOG_APPEND=true
|
||||||
|
```
|
||||||
|
|
||||||
|
### Overwrite Mode
|
||||||
|
|
||||||
|
Set `LOG_APPEND=false` to truncate the log file on each application start:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export LOG_APPEND=false
|
||||||
|
```
|
||||||
|
|
||||||
|
### Closing Log Files
|
||||||
|
|
||||||
|
Always close log files when shutting down to ensure all buffered logs are flushed:
|
||||||
|
|
||||||
|
```go
|
||||||
|
defer logger.CloseLogFile()
|
||||||
|
```
|
||||||
|
|
||||||
|
Or handle it explicitly:
|
||||||
|
|
||||||
|
```go
|
||||||
|
if err := logger.CloseLogFile(); err != nil {
|
||||||
|
log.Printf("Error closing log file: %v", err)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Structured Logging
|
||||||
|
|
||||||
|
HLog leverages zerolog's structured logging capabilities:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Simple message
|
||||||
|
logger.Info().Msg("User logged in")
|
||||||
|
|
||||||
|
// With fields
|
||||||
|
logger.Info().
|
||||||
|
Str("username", "john_doe").
|
||||||
|
Int("user_id", 12345).
|
||||||
|
Msg("User logged in")
|
||||||
|
|
||||||
|
// With error
|
||||||
|
logger.Error().
|
||||||
|
Err(err).
|
||||||
|
Str("operation", "database_query").
|
||||||
|
Msg("Operation failed")
|
||||||
|
|
||||||
|
// Nested objects
|
||||||
|
logger.Info().
|
||||||
|
Str("username", "john_doe").
|
||||||
|
Dict("metadata", zerolog.Dict().
|
||||||
|
Str("ip", "192.168.1.1").
|
||||||
|
Int("port", 8080),
|
||||||
|
).
|
||||||
|
Msg("Connection established")
|
||||||
|
```
|
||||||
|
|
||||||
|
### Field Types
|
||||||
|
|
||||||
|
HLog supports all zerolog field types:
|
||||||
|
|
||||||
|
- `Str(key, value)` - String
|
||||||
|
- `Int(key, value)` - Integer
|
||||||
|
- `Float64(key, value)` - Float
|
||||||
|
- `Bool(key, value)` - Boolean
|
||||||
|
- `Err(err)` - Error with stack trace
|
||||||
|
- `Time(key, value)` - Timestamp
|
||||||
|
- `Dur(key, duration)` - Duration
|
||||||
|
- `Dict(key, dict)` - Nested object
|
||||||
|
|
||||||
|
## Advanced Usage
|
||||||
|
|
||||||
|
### Custom Writers
|
||||||
|
|
||||||
|
You can use any `io.Writer` for console output:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Write to a buffer
|
||||||
|
var buf bytes.Buffer
|
||||||
|
logger, err := hlog.NewLogger(cfg, &buf)
|
||||||
|
|
||||||
|
// Write to multiple destinations
|
||||||
|
multiWriter := io.MultiWriter(os.Stdout, &buf)
|
||||||
|
logger, err := hlog.NewLogger(cfg, multiWriter)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Context Loggers
|
||||||
|
|
||||||
|
Create sub-loggers with additional context:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Create a logger with request context
|
||||||
|
requestLogger := logger.With().
|
||||||
|
Str("request_id", requestID).
|
||||||
|
Str("user_id", userID).
|
||||||
|
Logger()
|
||||||
|
|
||||||
|
requestLogger.Info().Msg("Processing request")
|
||||||
|
```
|
||||||
|
|
||||||
|
### Conditional Logging
|
||||||
|
|
||||||
|
Check if a log level is enabled before expensive operations:
|
||||||
|
|
||||||
|
```go
|
||||||
|
if logger.Debug().Enabled() {
|
||||||
|
// Only compute expensive debug info if debug logging is enabled
|
||||||
|
debugInfo := computeExpensiveDebugInfo()
|
||||||
|
logger.Debug().Interface("debug_info", debugInfo).Msg("Debug information")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Integration
|
||||||
|
|
||||||
|
HLog is designed to work seamlessly with other golib packages:
|
||||||
|
|
||||||
|
### With env Package
|
||||||
|
|
||||||
|
HLog uses the [env](https://git.haelnorr.com/h/golib/env) package for environment variable parsing in `ConfigFromEnv()`. The env package provides type-safe environment variable access with defaults.
|
||||||
|
|
||||||
|
### With hws/hwsauth
|
||||||
|
|
||||||
|
HLog integrates directly with the [hws](https://git.haelnorr.com/h/golib/hws) web server framework. The hws package provides an `AddLogger` function that accepts an `hlog.Logger` for structured logging throughout your web application:
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"git.haelnorr.com/h/golib/hlog"
|
||||||
|
"git.haelnorr.com/h/golib/hws"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Configure logger
|
||||||
|
cfg := &hlog.Config{
|
||||||
|
LogLevel: hlog.InfoLevel,
|
||||||
|
LogOutput: "both",
|
||||||
|
LogDir: "/var/log/myapp",
|
||||||
|
LogFileName: "server.log",
|
||||||
|
}
|
||||||
|
|
||||||
|
logger, err := hlog.NewLogger(cfg, os.Stdout)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
defer logger.CloseLogFile()
|
||||||
|
|
||||||
|
// Create HWS server configuration
|
||||||
|
hwsConfig := &hws.Config{
|
||||||
|
// ... your hws config
|
||||||
|
}
|
||||||
|
|
||||||
|
server, err := hws.NewServer(hwsConfig)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the logger to the server
|
||||||
|
server.AddLogger(logger)
|
||||||
|
|
||||||
|
// The server will now use hlog for all logging
|
||||||
|
logger.Info().Msg("Starting web server")
|
||||||
|
server.Start()
|
||||||
|
```
|
||||||
|
|
||||||
|
The hws framework will automatically use the provided logger for request logging, error logging, and other server events, ensuring consistent structured logging throughout your application.
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
1. **Always close log files**: Use `defer logger.CloseLogFile()` immediately after creating the logger to ensure logs are flushed on shutdown.
|
||||||
|
|
||||||
|
2. **Use structured logging**: Add context fields instead of formatting strings:
|
||||||
|
```go
|
||||||
|
// Good
|
||||||
|
logger.Info().Str("user", username).Int("age", age).Msg("User created")
|
||||||
|
|
||||||
|
// Avoid
|
||||||
|
logger.Info().Msgf("User created: %s, age: %d", username, age)
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Choose appropriate log levels**: Don't log everything at `Info` level. Use `Debug` for development details and `Trace` for very verbose output.
|
||||||
|
|
||||||
|
4. **Use ConfigFromEnv in production**: Environment-based configuration makes it easy to change logging behavior without code changes.
|
||||||
|
|
||||||
|
5. **Add context to loggers**: Create sub-loggers with request-specific context rather than adding the same fields to every log call.
|
||||||
|
|
||||||
|
6. **Avoid logging sensitive data**: Never log passwords, tokens, or other sensitive information.
|
||||||
|
|
||||||
|
7. **Use append mode in production**: Set `LOG_APPEND=true` to preserve logs across restarts for better debugging and auditing.
|
||||||
|
|
||||||
|
8. **Check log level before expensive operations**: Use `logger.Level().Enabled()` to avoid computing log messages that won't be written.
|
||||||
|
|
||||||
|
9. **Centralize logger creation**: Create your logger once at application startup and pass it through your application (dependency injection or context).
|
||||||
|
|
||||||
|
10. **Use appropriate output modes**:
|
||||||
|
- Development: `console` for immediate feedback
|
||||||
|
- Production: `both` for real-time monitoring and persistent logs
|
||||||
|
- Services: `file` when logs are collected by external tools
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### "LOG_DIR must be set when LOG_OUTPUT is 'file' or 'both'"
|
||||||
|
|
||||||
|
This error occurs when file logging is enabled but no directory is specified. Set the `LOG_DIR` environment variable:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export LOG_DIR=/var/log/myapp
|
||||||
|
```
|
||||||
|
|
||||||
|
### "LOG_FILE_NAME must be set when LOG_OUTPUT is 'file' or 'both'"
|
||||||
|
|
||||||
|
This error occurs when file logging is enabled but no filename is specified. Set the `LOG_FILE_NAME` environment variable:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export LOG_FILE_NAME=server.log
|
||||||
|
```
|
||||||
|
|
||||||
|
### "Invalid log level specified"
|
||||||
|
|
||||||
|
Valid log levels are: `trace`, `debug`, `info`, `warn`, `error`, `fatal`, `panic`. Check your `LOG_LEVEL` environment variable:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export LOG_LEVEL=info
|
||||||
|
```
|
||||||
|
|
||||||
|
### "Invalid LOG_OUTPUT"
|
||||||
|
|
||||||
|
Valid output modes are: `console`, `file`, `both`. Check your `LOG_OUTPUT` environment variable:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export LOG_OUTPUT=console
|
||||||
|
```
|
||||||
|
|
||||||
|
### Log file permission errors
|
||||||
|
|
||||||
|
Ensure the application has write permissions to the log directory:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir -p /var/log/myapp
|
||||||
|
chmod 755 /var/log/myapp
|
||||||
|
```
|
||||||
|
|
||||||
|
### Logs not appearing
|
||||||
|
|
||||||
|
1. Check that your log level includes the messages you're trying to log
|
||||||
|
2. Ensure you're not calling `logger.CloseLogFile()` before logging
|
||||||
|
3. Verify that stdout/stderr is not being redirected elsewhere
|
||||||
|
4. For file logging, check that the file exists and is being written to
|
||||||
|
|
||||||
|
### Logs not flushed on crash
|
||||||
|
|
||||||
|
When a program crashes or is killed, buffered logs may not be written. Consider:
|
||||||
|
- Using `both` output mode to see logs in real-time on console
|
||||||
|
- Calling `logger.CloseLogFile()` explicitly before risky operations
|
||||||
|
- Using `defer logger.CloseLogFile()` to ensure cleanup on normal exits
|
||||||
|
|
||||||
|
## See Also
|
||||||
|
|
||||||
|
- [env](https://git.haelnorr.com/h/golib/env) - Environment variable helpers
|
||||||
|
- [hws](https://git.haelnorr.com/h/golib/hws) - HTTP web server framework
|
||||||
|
- [hwsauth](https://git.haelnorr.com/h/golib/hwsauth) - Authentication middleware
|
||||||
|
|
||||||
|
## Links
|
||||||
|
|
||||||
|
- [GoDoc API Documentation](https://pkg.go.dev/git.haelnorr.com/h/golib/hlog)
|
||||||
|
- [Source Code](https://git.haelnorr.com/h/golib/hlog)
|
||||||
|
- [Issue Tracker](https://git.haelnorr.com/h/golib/issues)
|
||||||
|
- [Zerolog Documentation](https://github.com/rs/zerolog)
|
||||||
3
Home.md
3
Home.md
@@ -4,6 +4,9 @@ Welcome to the golib documentation wiki. This wiki provides comprehensive docume
|
|||||||
|
|
||||||
## Modules
|
## Modules
|
||||||
|
|
||||||
|
### [HLog](HLog.md)
|
||||||
|
Structured logging package built on top of zerolog. Provides simple configuration via environment variables, flexible output options (console, file, or both), and automatic log file management with high performance.
|
||||||
|
|
||||||
### [HWS](HWS.md)
|
### [HWS](HWS.md)
|
||||||
H Web Server - A lightweight, opinionated HTTP web server framework for Go built on top of the standard library. Features Go 1.22+ routing patterns, built-in middleware, structured error handling, and production-ready defaults.
|
H Web Server - A lightweight, opinionated HTTP web server framework for Go built on top of the standard library. Features Go 1.22+ routing patterns, built-in middleware, structured error handling, and production-ready defaults.
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user