|
| 1 | +# logfile |
| 2 | + |
| 3 | +Package logfile provides file-based log writers with automatic rotation capabilities for the [golog](https://github.com/domonda/golog) logging library. |
| 4 | + |
| 5 | +## Features |
| 6 | + |
| 7 | +- **Automatic File Rotation**: Rotates log files based on size thresholds |
| 8 | +- **Thread-Safe**: Safe for concurrent use by multiple goroutines |
| 9 | +- **Timestamp-Based Naming**: Rotated files are named with timestamps for easy identification |
| 10 | +- **Configurable**: Customizable file paths, permissions, rotation sizes, and time formats |
| 11 | +- **Seamless Integration**: Works with any `io.Writer` compatible logging system |
| 12 | + |
| 13 | +## Installation |
| 14 | + |
| 15 | +```bash |
| 16 | +go get github.com/domonda/golog/logfile |
| 17 | +``` |
| 18 | + |
| 19 | +## Quick Start |
| 20 | + |
| 21 | +### Basic Usage |
| 22 | + |
| 23 | +```go |
| 24 | +package main |
| 25 | + |
| 26 | +import ( |
| 27 | + "os" |
| 28 | + |
| 29 | + "github.com/domonda/golog" |
| 30 | + "github.com/domonda/golog/logfile" |
| 31 | +) |
| 32 | + |
| 33 | +func main() { |
| 34 | + // Create a rotating writer that rotates at 10MB |
| 35 | + writer, err := logfile.NewRotatingWriter( |
| 36 | + "/var/log/myapp.log", // File path |
| 37 | + logfile.RotatingWriterDefaultTimeFormat, // Time format for rotated files |
| 38 | + 0644, // File permissions |
| 39 | + 10*1024*1024, // Rotate at 10MB |
| 40 | + ) |
| 41 | + if err != nil { |
| 42 | + panic(err) |
| 43 | + } |
| 44 | + defer writer.Close() |
| 45 | + |
| 46 | + // Use with golog |
| 47 | + config := golog.NewConfig( |
| 48 | + &golog.DefaultLevels, |
| 49 | + golog.AllLevelsActive, |
| 50 | + golog.NewJSONWriterConfig(writer, nil), |
| 51 | + ) |
| 52 | + |
| 53 | + log := golog.NewLogger(config) |
| 54 | + |
| 55 | + log.Info("Application started").Log() |
| 56 | + log.Error("An error occurred").Err(err).Log() |
| 57 | +} |
| 58 | +``` |
| 59 | + |
| 60 | +### Multiple Writers (Console + Rotating File) |
| 61 | + |
| 62 | +```go |
| 63 | +package main |
| 64 | + |
| 65 | +import ( |
| 66 | + "os" |
| 67 | + |
| 68 | + "github.com/domonda/golog" |
| 69 | + "github.com/domonda/golog/logfile" |
| 70 | +) |
| 71 | + |
| 72 | +func main() { |
| 73 | + // Create rotating file writer |
| 74 | + fileWriter, err := logfile.NewRotatingWriter( |
| 75 | + "/var/log/myapp.log", |
| 76 | + "", // Use default time format |
| 77 | + 0644, // File permissions |
| 78 | + 5*1024*1024, // Rotate at 5MB |
| 79 | + ) |
| 80 | + if err != nil { |
| 81 | + panic(err) |
| 82 | + } |
| 83 | + defer fileWriter.Close() |
| 84 | + |
| 85 | + // Configure logger with both console and file output |
| 86 | + config := golog.NewConfig( |
| 87 | + &golog.DefaultLevels, |
| 88 | + golog.AllLevelsActive, |
| 89 | + // Colored text output to console |
| 90 | + golog.NewTextWriterConfig( |
| 91 | + os.Stdout, |
| 92 | + nil, |
| 93 | + golog.NewStyledColorizer(), |
| 94 | + ), |
| 95 | + // JSON output to rotating file |
| 96 | + golog.NewJSONWriterConfig(fileWriter, nil), |
| 97 | + ) |
| 98 | + |
| 99 | + log := golog.NewLogger(config) |
| 100 | + |
| 101 | + // Logs will appear on console in color AND be written to file in JSON |
| 102 | + log.Info("Server starting"). |
| 103 | + Str("host", "localhost"). |
| 104 | + Int("port", 8080). |
| 105 | + Log() |
| 106 | +} |
| 107 | +``` |
| 108 | + |
| 109 | +### Different Log Levels to Different Files |
| 110 | + |
| 111 | +```go |
| 112 | +package main |
| 113 | + |
| 114 | +import ( |
| 115 | + "github.com/domonda/golog" |
| 116 | + "github.com/domonda/golog/logfile" |
| 117 | +) |
| 118 | + |
| 119 | +func main() { |
| 120 | + // All logs file (rotates at 10MB) |
| 121 | + allLogsWriter, _ := logfile.NewRotatingWriter( |
| 122 | + "/var/log/myapp.log", |
| 123 | + "", |
| 124 | + 0644, |
| 125 | + 10*1024*1024, |
| 126 | + ) |
| 127 | + defer allLogsWriter.Close() |
| 128 | + |
| 129 | + // Errors only file (rotates at 5MB) |
| 130 | + errorLogsWriter, _ := logfile.NewRotatingWriter( |
| 131 | + "/var/log/myapp-errors.log", |
| 132 | + "", |
| 133 | + 0644, |
| 134 | + 5*1024*1024, |
| 135 | + ) |
| 136 | + defer errorLogsWriter.Close() |
| 137 | + |
| 138 | + // Configure with level filtering |
| 139 | + config := golog.NewConfig( |
| 140 | + &golog.DefaultLevels, |
| 141 | + golog.AllLevelsActive, |
| 142 | + // All logs |
| 143 | + golog.NewJSONWriterConfig(allLogsWriter, nil), |
| 144 | + // Errors and above only |
| 145 | + golog.NewJSONWriterConfig( |
| 146 | + errorLogsWriter, |
| 147 | + nil, |
| 148 | + golog.LevelFilterFrom(golog.DefaultLevels.Error), |
| 149 | + ), |
| 150 | + ) |
| 151 | + |
| 152 | + log := golog.NewLogger(config) |
| 153 | + |
| 154 | + log.Info("This goes to all logs").Log() |
| 155 | + log.Error("This goes to both all logs and error logs").Log() |
| 156 | +} |
| 157 | +``` |
| 158 | + |
| 159 | +## How Rotation Works |
| 160 | + |
| 161 | +When a log file reaches the configured size threshold: |
| 162 | + |
| 163 | +1. The current file is closed |
| 164 | +2. The file is renamed with a timestamp suffix (e.g., `myapp.log.2024-01-15_10:30:45`) |
| 165 | +3. If a file with that name already exists, a numeric suffix is added (e.g., `myapp.log.2024-01-15_10:30:45.1`) |
| 166 | +4. A new file is created at the original path |
| 167 | +5. Logging continues to the new file |
| 168 | + |
| 169 | +### Example File Rotation |
| 170 | + |
| 171 | +``` |
| 172 | +# Before rotation |
| 173 | +myapp.log (10MB) |
| 174 | +
|
| 175 | +# After first rotation |
| 176 | +myapp.log (0 bytes, new file) |
| 177 | +myapp.log.2024-01-15_10:30:45 (10MB) |
| 178 | +
|
| 179 | +# After second rotation (same second) |
| 180 | +myapp.log (0 bytes, new file) |
| 181 | +myapp.log.2024-01-15_10:30:45 (10MB) |
| 182 | +myapp.log.2024-01-15_10:30:45.1 (10MB) |
| 183 | +``` |
| 184 | + |
| 185 | +## Configuration Options |
| 186 | + |
| 187 | +### NewRotatingWriter Parameters |
| 188 | + |
| 189 | +```go |
| 190 | +func NewRotatingWriter( |
| 191 | + filePath string, // Path to log file |
| 192 | + timeFormat string, // Time format for rotated files (empty = default) |
| 193 | + filePerm os.FileMode, // File permissions (e.g., 0644) |
| 194 | + rotateSize int64, // Size in bytes (0 = no rotation) |
| 195 | +) (*RotatingWriter, error) |
| 196 | +``` |
| 197 | + |
| 198 | +### Time Format |
| 199 | + |
| 200 | +The `timeFormat` parameter uses Go's time layout format. Common formats: |
| 201 | + |
| 202 | +```go |
| 203 | +// Default format (recommended) |
| 204 | +logfile.RotatingWriterDefaultTimeFormat // "2006-01-02_15:04:05" |
| 205 | + |
| 206 | +// Date only |
| 207 | +"2006-01-02" // myapp.log.2024-01-15 |
| 208 | + |
| 209 | +// Date and hour |
| 210 | +"2006-01-02_15" // myapp.log.2024-01-15_10 |
| 211 | + |
| 212 | +// Custom format |
| 213 | +"2006-01-02_15-04-05" // myapp.log.2024-01-15_10-30-45 |
| 214 | +``` |
| 215 | + |
| 216 | +### Rotation Size Guidelines |
| 217 | + |
| 218 | +```go |
| 219 | +// No rotation (file grows indefinitely) |
| 220 | +0 |
| 221 | + |
| 222 | +// Common sizes |
| 223 | +1024 * 1024 // 1 MB |
| 224 | +5 * 1024 * 1024 // 5 MB |
| 225 | +10 * 1024 * 1024 // 10 MB |
| 226 | +100 * 1024 * 1024 // 100 MB |
| 227 | +1024 * 1024 * 1024 // 1 GB |
| 228 | +``` |
| 229 | + |
| 230 | +## Thread Safety |
| 231 | + |
| 232 | +`RotatingWriter` is fully thread-safe. All operations (Write, Sync, Close) are protected by an internal mutex, making it safe to use from multiple goroutines simultaneously. |
| 233 | + |
| 234 | +```go |
| 235 | +// Safe to use from multiple goroutines |
| 236 | +go func() { |
| 237 | + log.Info("From goroutine 1").Log() |
| 238 | +}() |
| 239 | + |
| 240 | +go func() { |
| 241 | + log.Info("From goroutine 2").Log() |
| 242 | +}() |
| 243 | +``` |
| 244 | + |
| 245 | +## Methods |
| 246 | + |
| 247 | +### Write([]byte) (int, error) |
| 248 | + |
| 249 | +Writes data to the log file. Automatically rotates if size threshold is exceeded. |
| 250 | + |
| 251 | +### Sync() error |
| 252 | + |
| 253 | +Flushes buffered data to disk. Useful for ensuring logs are persisted before shutdown. |
| 254 | + |
| 255 | +```go |
| 256 | +defer func() { |
| 257 | + writer.Sync() // Ensure all data is written |
| 258 | + // Do something else... |
| 259 | + writer.Close() // Note that Close also writes all buffered data to disk |
| 260 | +}() |
| 261 | +``` |
| 262 | + |
| 263 | +## Integration with Standard Library |
| 264 | + |
| 265 | +Since `RotatingWriter` implements `io.Writer`, it can be used with any logging library that accepts an `io.Writer`: |
| 266 | + |
| 267 | +```go |
| 268 | +// Standard library log |
| 269 | +import "log" |
| 270 | + |
| 271 | +writer, _ := logfile.NewRotatingWriter("/var/log/app.log", "", 0644, 10*1024*1024) |
| 272 | +defer writer.Close() |
| 273 | + |
| 274 | +log.SetOutput(writer) |
| 275 | +log.Println("This will be written to a rotating log file") |
| 276 | +``` |
| 277 | + |
| 278 | +## License |
| 279 | + |
| 280 | +This package is part of the [golog](https://github.com/domonda/golog) project and is licensed under the MIT License. |
| 281 | + |
| 282 | +## See Also |
| 283 | + |
| 284 | +- [golog documentation](https://pkg.go.dev/github.com/domonda/golog) |
| 285 | +- [Main golog README](https://github.com/domonda/golog) |
0 commit comments