// handleIgnoreOlder handles states which fall under ignore older // Based on the state information it is decided if the state information has to be updated or not func (p *ProspectorLog) handleIgnoreOlder(lastState, newState file.State) error { logp.Debug("prospector", "Ignore file because ignore_older reached: %s", newState.Source) if !lastState.IsEmpty() { if !lastState.Finished { logp.Info("File is falling under ignore_older before harvesting is finished. Adjust your close_* settings: %s", newState.Source) } // Old state exist, no need to update it return nil } // Make sure file is not falling under clean_inactive yet if p.isCleanInactive(newState) { logp.Debug("prospector", "Do not write state for ignore_older because clean_inactive reached") return nil } // Set offset to end of file to be consistent with files which were harvested before // See https://github.com/elastic/beats/pull/2907 newState.Offset = newState.Fileinfo.Size() // Write state for ignore_older file as none exists yet newState.Finished = true err := p.Prospector.updateState(input.NewEvent(newState)) if err != nil { return err } return nil }
// handleIgnoreOlder handles states which fall under ignore older // Based on the state information it is decided if the state information has to be updated or not func (p *ProspectorLog) handleIgnoreOlder(lastState, newState file.State) error { logp.Debug("prospector", "Ignore file because ignore_older reached: %s", newState.Source) if !lastState.IsEmpty() { if !lastState.Finished { logp.Info("File is falling under ignore_older before harvesting is finished. Adjust your close_* settings: %s", newState.Source) } // Old state exist, no need to update it return nil } // Make sure file is not falling under clean_inactive yet if p.isCleanInactive(newState) { logp.Debug("prospector", "Do not write state for ignore_older because clean_inactive reached") return nil } // Write state for ignore_older file as none exists yet newState.Finished = true err := p.Prospector.updateState(input.NewEvent(newState)) if err != nil { return err } return nil }
// startHarvester starts a new harvester with the given offset // In case the HarvesterLimit is reached, an error is returned func (p *Prospector) startHarvester(state file.State, offset int64) error { if p.config.HarvesterLimit > 0 && atomic.LoadUint64(&p.harvesterCounter) >= p.config.HarvesterLimit { harvesterSkipped.Add(1) return fmt.Errorf("Harvester limit reached.") } state.Offset = offset // Create harvester with state h, err := p.createHarvester(state) if err != nil { return err } p.wg.Add(1) // startHarvester is not run concurrently, but atomic operations are need for the decrementing of the counter // inside the following go routine atomic.AddUint64(&p.harvesterCounter, 1) go func() { defer func() { atomic.AddUint64(&p.harvesterCounter, ^uint64(0)) p.wg.Done() }() // Starts harvester and picks the right type. In case type is not set, set it to defeault (log) h.Harvest() }() return nil }
// harvestExistingFile continues harvesting a file with a known state if needed func (p *ProspectorLog) harvestExistingFile(newState file.State, oldState file.State) { logp.Debug("prospector", "Update existing file for harvesting: %s, offset: %v", newState.Source, oldState.Offset) // No harvester is running for the file, start a new harvester // It is important here that only the size is checked and not modification time, as modification time could be incorrect on windows // https://blogs.technet.microsoft.com/asiasupp/2010/12/14/file-date-modified-property-are-not-updating-while-modifying-a-file-without-closing-it/ if oldState.Finished && newState.Fileinfo.Size() > oldState.Offset { // Resume harvesting of an old file we've stopped harvesting from // This could also be an issue with force_close_older that a new harvester is started after each scan but not needed? // One problem with comparing modTime is that it is in seconds, and scans can happen more then once a second logp.Debug("prospector", "Resuming harvesting of file: %s, offset: %v", newState.Source, oldState.Offset) err := p.Prospector.startHarvester(newState, oldState.Offset) if err != nil { logp.Err("Harvester could not be started on existing file: %s, Err: %s", newState.Source, err) } return } // File size was reduced -> truncated file if oldState.Finished && newState.Fileinfo.Size() < oldState.Offset { logp.Debug("prospector", "Old file was truncated. Starting from the beginning: %s", newState.Source) err := p.Prospector.startHarvester(newState, 0) if err != nil { logp.Err("Harvester could not be started on truncated file: %s, Err: %s", newState.Source, err) } filesTrucated.Add(1) return } // Check if file was renamed if oldState.Source != "" && oldState.Source != newState.Source { // This does not start a new harvester as it is assume that the older harvester is still running // or no new lines were detected. It sends only an event status update to make sure the new name is persisted. logp.Debug("prospector", "File rename was detected: %s -> %s, Current offset: %v", oldState.Source, newState.Source, oldState.Offset) if oldState.Finished { logp.Debug("prospector", "Updating state for renamed file: %s -> %s, Current offset: %v", oldState.Source, newState.Source, oldState.Offset) // Update state because of file rotation oldState.Source = newState.Source event := input.NewEvent(oldState) err := p.Prospector.updateState(event) if err != nil { logp.Err("File rotation state update error: %s", err) } filesRenamed.Add(1) } else { logp.Debug("prospector", "File rename detected but harvester not finished yet.") } } if !oldState.Finished { // Nothing to do. Harvester is still running and file was not renamed logp.Debug("prospector", "Harvester for file is still running: %s", newState.Source) } else { logp.Debug("prospector", "File didn't change: %s", newState.Source) } }
// startHarvester starts a new harvester with the given offset // In case the HarvesterLimit is reached, an error is returned func (p *Prospector) startHarvester(state file.State, offset int64) error { if p.config.HarvesterLimit > 0 && atomic.LoadUint64(&p.harvesterCounter) >= p.config.HarvesterLimit { harvesterSkipped.Add(1) return fmt.Errorf("Harvester limit reached.") } state.Offset = offset // Set state to "not" finished to indicate that a harvester is running state.Finished = false // Create harvester with state h, err := p.createHarvester(state) if err != nil { return err } reader, err := h.Setup() if err != nil { return fmt.Errorf("Error setting up harvester: %s", err) } // State is directly updated and not through channel to make state update immediate // State is only updated after setup is completed successfully err = p.updateState(input.NewEvent(state)) if err != nil { return err } p.wg.Add(1) // startHarvester is not run concurrently, but atomic operations are need for the decrementing of the counter // inside the following go routine atomic.AddUint64(&p.harvesterCounter, 1) go func() { defer func() { atomic.AddUint64(&p.harvesterCounter, ^uint64(0)) p.wg.Done() }() // Starts harvester and picks the right type. In case type is not set, set it to defeault (log) h.Harvest(reader) }() return nil }
func (p *Prospector) startHarvester(state file.State, offset int64) (*harvester.Harvester, error) { state.Offset = offset // Create harvester with state h, err := p.createHarvester(state) if err != nil { return nil, err } p.wg.Add(1) go func() { defer p.wg.Done() // Starts harvester and picks the right type. In case type is not set, set it to defeault (log) h.Harvest() }() return h, nil }