/// <summary> /// Перебирает события в логах /// </summary> /// <param name="iteratorParams">Параметры итератора</param> /// <returns>Итератор для перебора событий</returns> private IEnumerable <EventRecord> GetEventsRundown(EventIteratorParams iteratorParams) { var logsIterator = new LogsIterator(_storageNamePredicate); var totalRead = 0; // перебираем логи в порядке убывания даты foreach (var helper in logsIterator.StreamedLogsRundown( iteratorParams.FromDate, iteratorParams.ToDate)) { // читаем лог с конца, для позиционирования в логе используем индекс // чтение выполняем блоками по _reverseBlockSize событий // определяем количество итераций по текущему логу var iterationsCount = helper.Index.RecordCount / _reverseBlockSize; if (helper.Index.RecordCount % _reverseBlockSize > 0) { iterationsCount++; } if (iterationsCount == 0) { // пустой лог continue; } for (var i = 0L; i < iterationsCount; i++) { // флаг принудительной остановки чтения лога, если он не дописан var stopReadThisLog = false; // определяем индекс первой записи в блоке (начинается с нуля) var firstRecordOfBlock = helper.Index.RecordCount - (i + 1) * _reverseBlockSize; // и число событий, которые нужно считать var eventsToRead = _reverseBlockSize; if (firstRecordOfBlock < 0) { // если попали сюда, это означает, что число записей в логе // не кратно _reverseBlockSize и нужно уточнить eventsToRead: eventsToRead = _reverseBlockSize + firstRecordOfBlock; // номер первой записи в этом случае равен нулю: firstRecordOfBlock = 0; } // переходим к нужному куску индексированных данных helper.Index.Seek(firstRecordOfBlock); // читаем очередной блок записей var bufferedEvents = new EventRecord[eventsToRead]; try { // флаг принудительного поиска по логу var seekInLog = true; for (var j = 0L; j < eventsToRead; j++) { // определяем индексированные данные очередной записи var currentIndex = helper.Index.GetNext(); if (seekInLog) { // устанавливаем смещение в логе на начало очередной записи helper.Reader.Seek(currentIndex.Offset, SeekOrigin.Begin); seekInLog = false; } // читаем очередное сообщение for (var k = 0; k < currentIndex.LinesCount; k++) { var storageEntry = EventRecordHelper.GetRawEntry( helper.Reader.ReadLine()); if (!iteratorParams.SourceFilter.Contains(storageEntry[3].TrimEnd()) || !iteratorParams.EventTypeFilter.Contains(storageEntry[4].TrimEnd())) { // прерываем чтение этого сообщения и взводим флаг принудительного // поиска по логу seekInLog = true; break; } if (bufferedEvents[j] == null) { bufferedEvents[j] = EventRecordHelper.CreateFromStorageEntry( storageEntry); } else { bufferedEvents[j].Text.Add(storageEntry[5]); } } } } catch (IndexOutOfRangeException) { // попали в недописанный кусок лога stopReadThisLog = true; } // разворачиваем блок записей Array.Reverse(bufferedEvents); // возвращаем записи как результат работы итератора foreach (var eventRecord in bufferedEvents) { if (eventRecord == null) { // пропускаем события, не попавшие под фильтр continue; } // возвращаем событие yield return(eventRecord); // увеличиваем счетчик событий totalRead++; // достигли максимального количества событий if (totalRead == iteratorParams.MaxEvents) { yield break; } } if (stopReadThisLog) { break; } } } }
/// <summary> /// Переиндексация /// </summary> /// <param name="validationResult">Результат валидации индекса</param> /// <param name="logFile">Имя лог-файла</param> /// <param name="indexName">Имя индекса</param> private void ReIndex(IndexValidationResult validationResult, string logFile, string indexName) { if (validationResult.State == IndexState.Corrupted && File.Exists(indexName)) { // удаляем существующий индекс File.Delete(indexName); } using (var source = OpenForReading(logFile)) using (var index = OpenForWriting(indexName)) { var recordId = string.Empty; var recordOffset = 0L; var recordLines = 0; var reader = new LineReader(source); using (var writer = new BinaryWriter(index, Encoding.Default)) { // записываем размер исходного лога writer.Seek(0, SeekOrigin.Begin); writer.Write(source.Length); switch (validationResult.State) { case IndexState.Corrupted: // оставляем место для количества записей в исходном логе writer.Write(validationResult.RecordsCount); break; case IndexState.Obsolete: // подсчет числа записей начнем с последней известной // записи в логе validationResult.RecordsCount--; // перемещаемся к началу последней записи в логе source.Seek(validationResult.LastRecordOffset, SeekOrigin.Begin); // перемещаемся к концу индексного файла минус один элемент индекса // (начинаем переиндексацию с последней известной записи!) writer.Seek(-(int)IndexElementSize, SeekOrigin.End); break; } // читаем исходный лог до конца while (!reader.Eof) { // запоминаем текущую позицию в логе var currentOffset = source.Position; // читаем очередную строку лога var rawEntry = EventRecordHelper.GetRawEntry(reader.ReadLine()); // проверяем ее на валидность по числу полей if (!EventRecordHelper.IsValidEntry(rawEntry)) { // вероятнее всего, это - недозаписанное событие // в текущем логе, прерываем чтение break; } // если сохраненный идентификатор записи не совпадает с // идентификатором считанной строки, эта строка является // первой в записи if (string.Compare(recordId, rawEntry[0]) != 0) { if (!string.IsNullOrEmpty(recordId)) { // сведения о предыдущей записи нужно сохранить // в индексном файле writer.Write(recordOffset); writer.Write(recordLines); // обнуляем счетчик строк в записи recordLines = 0; } // увеличиваем счетчик записей в логе validationResult.RecordsCount++; recordId = rawEntry[0]; recordOffset = currentOffset; } // увеличиваем число строк в записи recordLines++; // последняя запись if (reader.Eof) { // сохраняем сведения о последней записи writer.Write(recordOffset); writer.Write(recordLines); } } // сохраняем итоговое количество записей в индексе writer.Seek(sizeof(Int64), SeekOrigin.Begin); writer.Write(validationResult.RecordsCount); } } }