@@ -11,6 +11,7 @@ import (
1111 "sync/atomic"
1212 "time"
1313
14+ "github.com/Meesho/go-core/metric"
1415 logger "github.com/rs/zerolog/log"
1516 "golang.org/x/sys/unix"
1617)
@@ -46,22 +47,24 @@ type SizeFileWriter struct {
4647 // Last Pwritev duration (for metrics tracking)
4748 lastPwritevDuration atomic.Int64 // Nanoseconds
4849
49- // Channel for completed files (for GCS upload )
50- completedFileChan chan <- string
50+ // Metric tags for emissions (e.g., from Config.MetricTags + event_name )
51+ metricTags [] string
5152}
5253
53- // NewSizeFileWriter creates a new SizeFileWriter with the given configuration
54- // completedFileChan is optional - if provided, completed files will be sent to this channel for upload
55- func NewSizeFileWriter (config Config , completedFileChan chan <- string ) (* SizeFileWriter , error ) {
54+ // NewSizeFileWriter creates a new SizeFileWriter with the given configuration.
55+ // Files are written with .log.tmp suffix during active write; renamed to .log on rotation/close.
56+ // metricTags are propagated to metric emissions (e.g., from MergeMetricTags(config.MetricTags, eventName))
57+ func NewSizeFileWriter (config Config , metricTags []string ) (* SizeFileWriter , error ) {
5658 // Extract base directory and filename
5759 baseDir , baseFileName , err := extractBasePathSize (config .LogFilePath )
5860 if err != nil {
5961 return nil , fmt .Errorf ("failed to extract base path: %w" , err )
6062 }
6163
6264 // Generate timestamped filename for initial file (consistent naming)
65+ // Use .log.tmp during active write; rename to .log on rotation/close
6366 timestamp := time .Now ().Format ("2006-01-02_15-04-05" )
64- initialPath := filepath .Join (baseDir , fmt .Sprintf ("%s_%s.log" , baseFileName , timestamp ))
67+ initialPath := filepath .Join (baseDir , fmt .Sprintf ("%s_%s.log.tmp " , baseFileName , timestamp ))
6568
6669 // Open initial file with preallocation (always starts at offset 0 for new files)
6770 file , err := openDirectIOSize (initialPath , config .PreallocateFileSize )
@@ -77,7 +80,7 @@ func NewSizeFileWriter(config Config, completedFileChan chan<- string) (*SizeFil
7780 baseDir : baseDir ,
7881 baseFileName : baseFileName ,
7982 preallocateFileSize : config .PreallocateFileSize ,
80- completedFileChan : completedFileChan ,
83+ metricTags : getBaseTags ( metricTags ) ,
8184 }
8285
8386 // New files always start at offset 0
@@ -108,15 +111,14 @@ func (fw *SizeFileWriter) WriteVectored(buffers [][]byte) (int, error) {
108111 n , err := writevAlignedWithOffset (fw .fd , buffers , offset )
109112 pwritevDuration := time .Since (pwritevStart )
110113
111- // Store write duration for metrics
112114 fw .lastPwritevDuration .Store (pwritevDuration .Nanoseconds ())
115+ metric .Timing (MetricFileWriterWriteDuration , pwritevDuration , fw .metricTags )
113116
114117 if err != nil {
115118 logger .Error ().Err (err ).Msgf ("writev failed (file=%s offset=%d)" , fw .filePath , offset )
116119 return n , err
117120 }
118121
119- // Update offset atomically after successful write
120122 fw .fileOffset .Add (int64 (n ))
121123
122124 return n , nil
@@ -135,7 +137,6 @@ func (fw *SizeFileWriter) Close() error {
135137 // We need to complete the rotation: swap files, then close both
136138 if fw .nextFile != nil && fw .file != nil {
137139 // Complete the rotation by swapping files
138- // This will send the current file to upload channel
139140 if err := fw .swapFiles (); err != nil && firstErr == nil {
140141 firstErr = fmt .Errorf ("failed to complete rotation during close: %w" , err )
141142 }
@@ -173,16 +174,9 @@ func (fw *SizeFileWriter) Close() error {
173174 firstErr = err
174175 }
175176
176- // Send completed file to upload channel (non-blocking) if it has data
177- if hasData && fw .completedFileChan != nil {
178- select {
179- case fw .completedFileChan <- completedFilePath :
180- // Successfully sent to channel
181- default :
182- // Channel full - log warning but don't block close
183- logger .Warn ().Msgf ("upload channel full, skipping upload (file=%s)" , completedFilePath )
184- fmt .Printf ("[WARNING] Upload channel full, skipping upload for %s\n " , completedFilePath )
185- }
177+ // Rename .tmp to .log (retry 3x, emit metric on failure)
178+ if hasData {
179+ renameTmpToLog (completedFilePath , fw .metricTags )
186180 }
187181
188182 fw .file = nil
@@ -249,9 +243,9 @@ func (fw *SizeFileWriter) rotateIfNeeded() error {
249243
250244// createNextFile creates a new file for rotation with preallocation
251245func (fw * SizeFileWriter ) createNextFile () error {
252- // Generate timestamped filename: {baseFileName}_{YYYY-MM-DD_HH-MM-SS}.log
246+ // Generate timestamped filename: {baseFileName}_{YYYY-MM-DD_HH-MM-SS}.log.tmp
253247 timestamp := time .Now ().Format ("2006-01-02_15-04-05" )
254- nextPath := filepath .Join (fw .baseDir , fmt .Sprintf ("%s_%s.log" , fw .baseFileName , timestamp ))
248+ nextPath := filepath .Join (fw .baseDir , fmt .Sprintf ("%s_%s.log.tmp " , fw .baseFileName , timestamp ))
255249
256250 // Try to open new file with preallocation
257251 file , err := openDirectIOSize (nextPath , fw .preallocateFileSize )
@@ -302,37 +296,29 @@ func (fw *SizeFileWriter) swapFiles() error {
302296 }
303297 }
304298
305- // Store current file path before closing (for upload)
299+ // Store current file path before closing
306300 completedFilePath := fw .filePath
307301
308302 // Close current file
309303 if err := fw .file .Close (); err != nil {
310304 return fmt .Errorf ("failed to close current file: %w" , err )
311305 }
312306
313- // Send completed file to upload channel (non-blocking)
314- if fw .completedFileChan != nil {
315- select {
316- case fw .completedFileChan <- completedFilePath :
317- // Successfully sent to channel
318- default :
319- // Channel full - log warning but don't block rotation
320- logger .Warn ().Msgf ("upload channel full, skipping upload (file=%s)" , completedFilePath )
321- fmt .Printf ("[WARNING] Upload channel full, skipping upload for %s\n " , completedFilePath )
322- }
323- }
307+ // Rename .tmp to .log (retry 3x, emit metric on failure)
308+ renameTmpToLog (completedFilePath , fw .metricTags )
324309
325310 // Swap next file to current
326311 fw .file = fw .nextFile
327312 fw .fd = fw .nextFd
328313 fw .filePath = fw .nextFilePath
329314 fw .fileOffset .Store (0 ) // Reset offset for new file
330315
331- // Clear next file fields
332316 fw .nextFile = nil
333317 fw .nextFd = 0
334318 fw .nextFilePath = ""
335319
320+ metric .Incr (MetricFileWriterRotationCount , fw .metricTags )
321+
336322 return nil
337323}
338324
0 commit comments