func (spool *spoolFile) getFileNumbering() (min int, max int) { min, max = maxSpoolFileNumber+1, 0 files, _ := ioutil.ReadDir(spool.basePath) for _, file := range files { base := filepath.Base(file.Name()) number, _ := shared.Btoi([]byte(base)) // Because we need leading zero support min = shared.MinI(min, int(number)) max = shared.MaxI(max, int(number)) } return min, max }
func (prod *File) getFileState(streamID core.MessageStreamID, forceRotate bool) (*fileState, error) { if state, stateExists := prod.filesByStream[streamID]; stateExists { if rotate, err := state.needsRotate(prod.rotate, forceRotate); !rotate { return state, err // ### return, already open or error ### } } var logFileName, fileDir, fileName, fileExt string if prod.wildcardPath { // Get state from filename (without timestamp, etc.) var streamName string switch streamID { case core.WildcardStreamID: streamName = "ALL" default: streamName = core.StreamRegistry.GetStreamName(streamID) } fileDir = strings.Replace(prod.fileDir, "*", streamName, -1) fileName = strings.Replace(prod.fileName, "*", streamName, -1) fileExt = strings.Replace(prod.fileExt, "*", streamName, -1) } else { // Simple case: only one file used fileDir = prod.fileDir fileName = prod.fileName fileExt = prod.fileExt } logFileBasePath := fmt.Sprintf("%s/%s%s", fileDir, fileName, fileExt) // Assure the file is correctly mapped state, stateExists := prod.files[logFileBasePath] if !stateExists { // state does not yet exist: create and map it state = newFileState(prod.batchMaxCount, prod.GetFormatter(), prod.Drop, prod.flushTimeout) prod.files[logFileBasePath] = state prod.filesByStream[streamID] = state } else if _, mappingExists := prod.filesByStream[streamID]; !mappingExists { // state exists but is not mapped: map it and see if we need to rotate prod.filesByStream[streamID] = state if rotate, err := state.needsRotate(prod.rotate, forceRotate); !rotate { return state, err // ### return, already open or error ### } } // Assure path is existing if err := os.MkdirAll(fileDir, 0755); err != nil { return nil, fmt.Errorf("Failed to create %s because of %s", fileDir, err.Error()) // ### return, missing directory ### } // Generate the log filename based on rotation, existing files, etc. if !prod.rotate.enabled { logFileName = fmt.Sprintf("%s%s", fileName, fileExt) } else { timestamp := time.Now().Format(prod.timestamp) signature := fmt.Sprintf("%s_%s", fileName, timestamp) maxSuffix := uint64(0) files, _ := ioutil.ReadDir(fileDir) for _, file := range files { if strings.HasPrefix(file.Name(), signature) { counter, _ := shared.Btoi([]byte(file.Name()[len(signature)+1:])) if maxSuffix <= counter { maxSuffix = counter + 1 } } } if maxSuffix == 0 { logFileName = fmt.Sprintf("%s%s", signature, fileExt) } else { logFileName = fmt.Sprintf("%s_%d%s", signature, int(maxSuffix), fileExt) } } logFilePath := fmt.Sprintf("%s/%s", fileDir, logFileName) // Close existing log if state.file != nil { currentLog := state.file state.file = nil if prod.rotate.compress { go state.compressAndCloseLog(currentLog) } else { Log.Note.Print("Rotated ", currentLog.Name(), " -> ", logFilePath) currentLog.Close() } } // (Re)open logfile var err error openFlags := os.O_WRONLY | os.O_CREATE | os.O_APPEND if prod.overwriteFile { openFlags |= os.O_TRUNC } else { openFlags |= os.O_APPEND } state.file, err = os.OpenFile(logFilePath, openFlags, prod.permissions) if err != nil { return state, err // ### return error ### } // Create "current" symlink state.fileCreated = time.Now() if prod.rotate.enabled { symLinkName := fmt.Sprintf("%s/%s_current%s", fileDir, fileName, fileExt) os.Remove(symLinkName) os.Symlink(logFileName, symLinkName) } // Prune old logs if requested switch { case prod.pruneCount > 0 && prod.pruneSize > 0: go func() { state.pruneByCount(logFileBasePath, prod.pruneCount) state.pruneToSize(logFileBasePath, prod.pruneSize) }() case prod.pruneCount > 0: go state.pruneByCount(logFileBasePath, prod.pruneCount) case prod.pruneSize > 0: go state.pruneToSize(logFileBasePath, prod.pruneSize) } return state, err }