private void ProcessNewOrExpandedFiles(string filePath, string relativeFilePath, long fileSize)
        {
            //Only process new or expanded files
            if (_logFiles.TryGetValue(relativeFilePath, out TContext context))
            {
                // If there is no registered bookmark and we're bookmarking on buffer flush,
                // that means the file was read and events buffered, but never uploaded by the source.
                var position = this.bookmarkOnBufferFlush
                    ? (BookmarkManager.GetBookmark(this.GetBookmarkName(filePath)) ?? BookmarkManager.RegisterBookmark(this.GetBookmarkName(filePath), 0, (pos) => this.SaveBookmark())).Position
                    : context.Position;

                if (fileSize > position)
                {
                    // The file is expanded compared with position saved in bookmark
                    AddToBuffer(relativeFilePath);
                }
            }
            else
            {
                //New file
                _logFiles.Add(relativeFilePath, CreateLogSourceInfo(filePath, 0));
                if (this.bookmarkOnBufferFlush)
                {
                    BookmarkManager.RegisterBookmark(this.GetBookmarkName(filePath), 0, (pos) => this.SaveBookmark());
                }
                AddToBuffer(relativeFilePath);
            }
        }
        protected virtual void OnRenamed(object source, RenamedEventArgs e)
        {
            try
            {
                // this is for Subdirectories check only
                if (this.includeSubdirectories && this.ShouldSkip(e.FullPath))
                {
                    return;
                }

                //Sometimes we receive event where e.name is null so we should just skip it
                if (string.IsNullOrEmpty(e.Name) || string.IsNullOrEmpty(e.OldName) ||
                    ShouldExclude(e.Name) || ShouldExclude(e.OldName) ||
                    (!ShouldInclude(e.Name) && !ShouldInclude(e.OldName)))
                {
                    return;
                }

                //File name rotation
                RemoveFromBuffer(e.OldName);
                if (_logFiles.ContainsKey(e.OldName))
                {
                    var newSourceInfo = CreateLogSourceInfo(e.FullPath, _logFiles[e.OldName].Position);
                    newSourceInfo.LineNumber = _logFiles[e.OldName].LineNumber;
                    _logFiles[e.Name]        = newSourceInfo;
                    _logFiles.Remove(e.OldName);

                    var bookmark = BookmarkManager.GetBookmark(this.GetBookmarkName(e.OldFullPath));
                    if (bookmark != null)
                    {
                        BookmarkManager.RemoveBookmark(bookmark.Id);
                        BookmarkManager.RegisterBookmark(this.GetBookmarkName(e.FullPath), bookmark.Position, (id) => this.SaveBookmark());
                    }
                }
                else
                {
                    var newSource = CreateLogSourceInfo(e.FullPath, 0);
                    _logFiles.Add(e.Name, newSource);
                    BookmarkManager.RegisterBookmark(this.GetBookmarkName(e.FullPath), 0, (id) => this.SaveBookmark());
                }
            }
            catch (Exception ex)
            {
                _logger?.LogError(ex.ToMinimized());
            }
            finally
            {
                AddToBuffer(e.Name);
                _logger?.LogInformation("File: {0} renamed to {1}", e.OldFullPath, e.FullPath);
            }
        }
        public void SaveBookmark()
        {
            if (this.InitialPosition == InitialPositionEnum.EOS || !this._started)
            {
                return;
            }

            // We don't gather the contents of the bookmark file outside of the lock because
            // we want to avoid a situation where two threads capture position info at slightly different times, and then they write the file out of sequence
            // (older collected data after newer collected data) since that would lead to out of date bookmarks recorded in the bookmark file.  In other words
            // the gathering of position data and writing the file needs to be atomic.
            lock (_bookmarkFileLock)
            {
                try
                {
                    using (var fs = new FileStream(this.bookmarkPath, FileMode.Create, FileAccess.Write, FileShare.None))
                        using (var sw = new StreamWriter(fs))
                        {
                            foreach (var logFile in _logFiles.Values)
                            {
                                long position;
                                if (this.bookmarkOnBufferFlush)
                                {
                                    position = BookmarkManager.GetBookmark(this.GetBookmarkName(logFile.FilePath))?.Position ?? logFile.Position;
                                }
                                else
                                {
                                    position = logFile.Position;
                                }

                                sw.WriteLine($"{logFile.FilePath},{position}");
                            }
                        }
                }
                catch (Exception ex)
                {
                    _logger?.LogError($"Failed saving bookmark: {ex.ToMinimized()}");
                }
            }
        }