github.com/qiniu/go-sdk/v7@v7.27.0/internal/uplog/uplog_buffer.go (about) 1 package uplog 2 3 import ( 4 "bytes" 5 "compress/gzip" 6 "io" 7 "os" 8 "path/filepath" 9 "strings" 10 "sync" 11 "sync/atomic" 12 "time" 13 14 "github.com/gofrs/flock" 15 ) 16 17 var ( 18 uplogDisabled bool 19 uplogChan chan<- uplogSerializedEntry 20 21 uplogFileBufferDirPath string 22 uplogFileBufferDirPathMutex sync.Mutex 23 24 uplogFileBuffer *os.File 25 uplogFileBufferFileLocker *flock.Flock 26 uplogFileBufferLock sync.Mutex 27 uplogFileBufferThreshold uint64 = 4 * 1024 * 1024 28 uplogMaxStorageBytes uint64 = 100 * 1024 * 1024 29 uplogWriteFileBufferTicker *time.Ticker 30 uplogWriteFileBufferInterval time.Duration = 1 * time.Minute 31 uplogWriteFileBufferTimerLock sync.Mutex 32 33 getUpToken GetUpToken 34 ) 35 36 const ( 37 UPLOG_CHANNEL_SIZE = 1024 38 UPLOG_MEMORY_BUFFER_SIZE = 4 * 1024 39 UPLOG_FILE_BUFFER_NAME = "uplog_v4_01.buffer" 40 UPLOG_FILE_LOCK_NAME = "uplog_v4_01.lock" 41 ) 42 43 type uplogSerializedEntry struct { 44 serializedUplog []byte 45 getUpToken GetUpToken 46 } 47 48 func init() { 49 uplogChannel := make(chan uplogSerializedEntry, UPLOG_CHANNEL_SIZE) 50 uplogWriteFileBufferTicker = time.NewTicker(uplogWriteFileBufferInterval) 51 go func() { 52 for { 53 select { 54 case serializedEntry := <-uplogChannel: 55 if uplogDisabled { 56 continue 57 } 58 if gut := serializedEntry.getUpToken; gut != nil { 59 getUpToken = gut 60 } 61 uplogBuffer := bytes.NewBuffer(make([]byte, 0, UPLOG_MEMORY_BUFFER_SIZE)) 62 uplogBuffer.Write(serializedEntry.serializedUplog) 63 uplogBuffer.WriteString("\n") 64 for uplogBuffer.Len() < (UPLOG_MEMORY_BUFFER_SIZE / 2) { 65 select { 66 case serializedEntry := <-uplogChannel: 67 uplogBuffer.Write(serializedEntry.serializedUplog) 68 uplogBuffer.WriteString("\n") 69 default: 70 goto finishReading 71 } 72 } 73 finishReading: 74 _, _ = writeMemoryBufferToFileBuffer(uplogBuffer.Bytes()) 75 case <-uplogWriteFileBufferTicker.C: 76 if fi, err := os.Stat(getUplogFileBufferPath(true)); err == nil && fi.Size() > 0 { 77 tryToArchiveFileBuffer(false) 78 } 79 } 80 } 81 }() 82 uplogChan = uplogChannel 83 } 84 85 func DisableUplog() { 86 uplogDisabled = true 87 } 88 89 func EnableUplog() { 90 uplogDisabled = false 91 } 92 93 func IsUplogEnabled() bool { 94 return !uplogDisabled 95 } 96 97 func GetUplogMaxStorageBytes() uint64 { 98 return atomic.LoadUint64(&uplogMaxStorageBytes) 99 } 100 101 func SetUplogMaxStorageBytes(max uint64) { 102 atomic.StoreUint64(&uplogMaxStorageBytes, max) 103 } 104 105 func SetUplogFileBufferDirPath(path string) { 106 uplogFileBufferDirPathMutex.Lock() 107 defer uplogFileBufferDirPathMutex.Unlock() 108 uplogFileBufferDirPath = path 109 } 110 111 func getUplogFileBufferPath(current bool) string { 112 var uplogFileBufferPath string 113 114 uplogFileBufferDirPathMutex.Lock() 115 defer uplogFileBufferDirPathMutex.Unlock() 116 117 if uplogFileBufferDirPath == "" { 118 uplogFileBufferPath = filepath.Join(os.TempDir(), "qiniu-golang-sdk", UPLOG_FILE_BUFFER_NAME) 119 } else { 120 uplogFileBufferPath = filepath.Join(uplogFileBufferDirPath, UPLOG_FILE_BUFFER_NAME) 121 } 122 if !current { 123 uplogFileBufferPath = uplogFileBufferPath + "." + time.Now().UTC().Format("20060102150405.999999999") 124 } 125 return uplogFileBufferPath 126 } 127 128 func getUplogFileDirectoryLock() *flock.Flock { 129 uplogFileBufferDirPathMutex.Lock() 130 defer uplogFileBufferDirPathMutex.Unlock() 131 var uplogFileLockPath string 132 if uplogFileBufferDirPath == "" { 133 uplogFileLockPath = filepath.Join(os.TempDir(), "qiniu-golang-sdk", UPLOG_FILE_LOCK_NAME) 134 } else { 135 uplogFileLockPath = filepath.Join(uplogFileBufferDirPath, UPLOG_FILE_LOCK_NAME) 136 } 137 return flock.New(uplogFileLockPath) 138 } 139 140 func FlushBuffer() error { 141 return withUploadFileBuffer(func(w io.WriteCloser) error { 142 return w.Close() 143 }) 144 } 145 146 func writeMemoryBufferToFileBuffer(data []byte) (n int, err error) { 147 if err = withUploadFileBuffer(func(w io.WriteCloser) (e error) { 148 for len(data) > 0 { 149 n, e = w.Write(data) 150 if e != nil { 151 return 152 } 153 data = data[n:] 154 } 155 return 156 }); err != nil { 157 return 158 } 159 160 if fi, serr := os.Stat(getUplogFileBufferPath(true)); serr == nil && uint64(fi.Size()) >= uplogFileBufferThreshold { 161 tryToArchiveFileBuffer(false) 162 } 163 return 164 } 165 166 func tryToArchiveFileBuffer(force bool) { 167 var ( 168 locked bool 169 err error 170 ) 171 172 if fileInfo, fileInfoErr := os.Stat(getUplogFileBufferPath(true)); fileInfoErr == nil && fileInfo.Size() == 0 { 173 return 174 } 175 176 locker := getUplogFileDirectoryLock() 177 if force { 178 if err = locker.Lock(); err != nil { 179 return 180 } 181 } else { 182 if locked, err = locker.TryLock(); err != nil || !locked { 183 return 184 } 185 } 186 defer locker.Close() 187 188 if err = withUploadFileBuffer(func(w io.WriteCloser) error { 189 currentFilePath := getUplogFileBufferPath(true) 190 if fileInfo, fileInfoErr := os.Stat(currentFilePath); fileInfoErr == nil && fileInfo.Size() == 0 { 191 return nil 192 } 193 archivedFilePath := getUplogFileBufferPath(false) 194 w.Close() 195 return os.Rename(currentFilePath, archivedFilePath) 196 }); err != nil { 197 return 198 } 199 200 resetWriteFileBufferInterval() 201 go uploadAllClosedFileBuffers() 202 } 203 204 type uplogFileBufferWrapper struct{} 205 206 func (uplogFileBufferWrapper) Write(p []byte) (int, error) { 207 return uplogFileBuffer.Write(p) 208 } 209 210 func (uplogFileBufferWrapper) Close() error { 211 return closeUplogFileBufferWithoutLock() 212 } 213 214 func withUploadFileBuffer(fn func(io.WriteCloser) error) (err error) { 215 uplogFileBufferLock.Lock() 216 defer uplogFileBufferLock.Unlock() 217 218 uplogFileBufferPath := getUplogFileBufferPath(true) 219 220 if uplogFileBuffer != nil { 221 if uplogFileBuffer.Name() != uplogFileBufferPath { 222 closeUplogFileBufferWithoutLock() 223 } else if _, err := os.Stat(uplogFileBufferPath); err != nil && os.IsNotExist(err) { 224 closeUplogFileBufferWithoutLock() 225 } 226 } 227 228 if uplogFileBuffer == nil { 229 if err = os.MkdirAll(filepath.Dir(uplogFileBufferPath), 0755); err != nil { 230 return 231 } else if uplogFileBuffer, err = os.OpenFile(uplogFileBufferPath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644); err != nil { 232 return 233 } 234 uplogFileBufferLockPath := uplogFileBufferPath + ".lock" 235 uplogFileBufferFileLocker = flock.New(uplogFileBufferLockPath) 236 } 237 238 if err = uplogFileBufferFileLocker.Lock(); err != nil { 239 return 240 } 241 err = fn(uplogFileBufferWrapper{}) 242 if uplogFileBufferFileLocker != nil && uplogFileBufferFileLocker.Locked() { 243 _ = uplogFileBufferFileLocker.Unlock() 244 } 245 return 246 } 247 248 func closeUplogFileBufferWithoutLock() error { 249 var err1, err2 error 250 if uplogFileBuffer != nil { 251 err1 = uplogFileBuffer.Close() 252 uplogFileBuffer = nil 253 } 254 if uplogFileBufferFileLocker != nil { 255 err2 = uplogFileBufferFileLocker.Close() 256 uplogFileBufferFileLocker = nil 257 } 258 if err1 != nil { 259 return err1 260 } 261 return err2 262 } 263 264 func SetWriteFileBufferInterval(d time.Duration) { 265 uplogWriteFileBufferTimerLock.Lock() 266 defer uplogWriteFileBufferTimerLock.Unlock() 267 if d == 0 { 268 d = 1 * time.Minute 269 } 270 uplogWriteFileBufferInterval = d 271 uplogWriteFileBufferTicker.Stop() 272 uplogWriteFileBufferTicker = time.NewTicker(d) 273 } 274 275 func GetWriteFileBufferInterval() time.Duration { 276 uplogWriteFileBufferTimerLock.Lock() 277 defer uplogWriteFileBufferTimerLock.Unlock() 278 279 return uplogWriteFileBufferInterval 280 } 281 282 func resetWriteFileBufferInterval() { 283 uplogWriteFileBufferTimerLock.Lock() 284 defer uplogWriteFileBufferTimerLock.Unlock() 285 uplogWriteFileBufferTicker.Stop() 286 uplogWriteFileBufferTicker = time.NewTicker(uplogWriteFileBufferInterval) 287 } 288 289 type multipleFileReader struct { 290 paths []string 291 292 compressor *gzip.Writer 293 w *io.PipeWriter 294 r *io.PipeReader 295 err error 296 errLock sync.Mutex 297 wg sync.WaitGroup 298 } 299 300 func newMultipleFileReader(paths []string) (*multipleFileReader, error) { 301 r, w := io.Pipe() 302 compressor, err := gzip.NewWriterLevel(w, gzip.BestCompression) 303 if err != nil { 304 return nil, err 305 } 306 mfr := &multipleFileReader{ 307 paths: paths, 308 r: r, 309 w: w, 310 compressor: compressor, 311 } 312 mfr.wg.Add(1) 313 go mfr.readAllAsync() 314 return mfr, nil 315 } 316 317 func (r *multipleFileReader) readAllAsync() { 318 defer r.wg.Done() 319 defer r.w.CloseWithError(io.EOF) 320 defer r.compressor.Close() 321 for _, path := range r.paths { 322 if err := r.readAllForPathAsync(path); err != nil { 323 r.setError(err) 324 return 325 } 326 } 327 } 328 329 func (r *multipleFileReader) readAllForPathAsync(path string) error { 330 file, err := os.Open(path) 331 if err != nil { 332 return err 333 } 334 defer file.Close() 335 if _, err = io.Copy(r.compressor, file); err != nil && err != io.EOF { 336 return err 337 } 338 return nil 339 } 340 341 func (r *multipleFileReader) getError() error { 342 r.errLock.Lock() 343 defer r.errLock.Unlock() 344 return r.err 345 } 346 347 func (r *multipleFileReader) setError(err error) { 348 r.errLock.Lock() 349 defer r.errLock.Unlock() 350 r.err = err 351 } 352 353 func (r *multipleFileReader) Read(p []byte) (int, error) { 354 if err := r.getError(); err != nil { 355 return 0, err 356 } 357 return r.r.Read(p) 358 } 359 360 func (r *multipleFileReader) Close() error { 361 err := r.r.CloseWithError(io.EOF) 362 r.wg.Wait() 363 return err 364 } 365 366 func getArchivedUplogFileBufferPaths(dirPath string) ([]string, error) { 367 dirEntries, err := os.ReadDir(dirPath) 368 if err != nil { 369 return nil, err 370 } 371 372 archivedPaths := make([]string, 0, len(dirEntries)) 373 for _, dirEntry := range dirEntries { 374 if !dirEntry.Type().IsRegular() || 375 !strings.HasPrefix(dirEntry.Name(), UPLOG_FILE_BUFFER_NAME+".") || 376 strings.HasSuffix(dirEntry.Name(), ".lock") { 377 continue 378 } 379 archivedPaths = append(archivedPaths, filepath.Join(dirPath, dirEntry.Name())) 380 } 381 return archivedPaths, nil 382 }