Использование MemoryMappedFile и FileSystemWatcher для обнаружения новых записей в файле журнала
-
11-10-2019 - |
Вопрос
У меня есть файл журнала, написанный сторонним приложением, и я бы хотел, чтобы мое приложение "читало" этот файл журнала в режиме реального / ближайшего времени, анализировало новые записи журнала и действовало в соответствии с определенными событиями.
Я думал, что я мог бы достичь этого с помощью комбинации FileSystemWatcher (для сигнализации изменений файла) и MemoryMappedFile (для продолжения чтения с определенного смещения).
Однако, поскольку я впервые использую MemoryMappedFiles, я сталкиваюсь с некоторыми проблемами, которые, вероятно, возникают из-за неправильного понимания концепции (напримерЯ не могу открыть существующий файл, так как он используется другим процессом).
Мне было интересно, есть ли у кого-нибудь пример того, как использовать MemoryMappedFiles для чтения файла, который заблокирован другим процессом?
Спасибо,
Том
Редактировать:
Судя по комментариям, похоже, что файлы с отображением в памяти не помогут мне получить доступ к файлам с эксклюзивной блокировкой.Однако "хвостовые" инструменты, такие как, напримерBaretail (http://www.baremetalsoft.com/baretail/index.php) способны именно на это.У него нет проблем с чтением файла с эксклюзивной блокировкой из другого приложения с интервалом в 1 секунду).Значит, должен быть какой-то способ сделать это?
ОТРЕДАКТИРОВАННЫЙ ТЕКСТ:
Чтобы ответить на мой собственный вопрос, хитрость при открытии заблокированного файла заключается в создании FileStream со следующими флагами доступа:
fileStream = new System.IO.FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Delete | FileShare.ReadWrite);
Решение
Чтобы ответить на мой собственный вопрос, трюк в чтении заблокированного файла создает FileStream со следующими флагами доступа:
FileStream fileStream = new System.IO.FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Delete | FileShare.ReadWrite);
Теперь это просто вопрос либо проведения опроса на основе интервалов, либо поиск событий FileSystemWatcher Изменения для обнаружения изменений файла
Другие советы
Я не уверен, что MemoryMappedFiles поможет вам. Взгляните на FileStream:
var stream = new FileStream(path, FileMode.Open, FileShare.Read);
stream.Seek(offset, SeekOrigin.Begin);
Хотя, если приложение для третьей стороны имеет файл заблокирован исключительно, вы не можете с этим сделать ...
[Начать 2-ю ПРАВКУ]
Еще одна идея...
Если стороннее приложение использует платформу ведения журнала, такую как NLog, log4net или System.При диагностике вы все равно могли бы написать свой собственный Target / Appender / TraceListener и направлять сообщения куда-нибудь, где вы могли бы их просмотреть (например, в файл, который не открывается исключительно, в другой процесс и т.д.).
Если бы ваше стороннее приложение использовало платформу ведения журнала, мы, вероятно, уже слышали бы об этом ;-)
[Окончание 2-й ПРАВКИ]
[Начать ПРАВКУ]
Мне кажется, я неправильно истолковал вопрос.Сначала это звучало так, как будто вы использовали стороннюю библиотеку, в которой было реализовано ведение журнала, и вы хотели выполнить этот синтаксический анализ из программы, которая генерировала ведение журнала.Перечитав ваш вопрос, я понял, что вы хотите "прослушать" файл журнала извне приложения.Если это так, то мой ответ, вероятно, вам не поможет.Извините.
[Окончательная ПРАВКА]
Мне нечего предложить о MemoryMappedFiles, но мне интересно, могли бы вы достичь того, к чему стремитесь, написав пользовательский прослушиватель / target / appender для сторонней системы ведения журнала?
Например, если вы используете NLog, вы могли бы написать пользовательскую цель и направлять туда все ваши сообщения журнала (одновременно направляя их на "реальную" цель (цели)).Таким образом, вы получаете доступ к каждому сообщению журнала по мере его регистрации (так что на самом деле это происходит в режиме реального времени, а не вблизи реального времени).Вы могли бы сделать то же самое с log4net и System.Диагностика.
Обратите внимание, что у NLog даже есть цель "MethodCall".Чтобы использовать его, вам нужно всего лишь написать статический метод с правильной сигнатурой.Я не знаю, есть ли в log4net похожая концепция на эту.
Похоже, что было бы проще обеспечить надежную работу, чем пытаться читать и анализировать файл журнала, поскольку он записывается сторонним программным обеспечением.
Если файл «используется», с этим нет ничего, что можно сделать. Это действительно "в использовании". Memory Mamepoppels предназначены для чтения больших объемов данных с диска или обмена данными с другими программами. Это не поможет обойти ограничение «в использовании».
Файлы, нанесенные на карту памяти, находятся под теми же ограничениями, что и FileStream, с которым вы его инициализируете, убедитесь, что вы инициализируете свою картину памяти, как это
var ReaderStream = new FileStream (path, fileMode.open, fileCcess.read, fileshare.readwrite);
var mmf = memoryMappedFile.createFromFile (readerStream, null, 0, memoryMappedFileAccess.read, null, handlenhibality.none, false);
Если какой -то другой процесс полностью заблокировал его даже от написания, вам не повезло, не уверен, есть ли это способ обойти это. Возможно, используйте какой -то таймер, чтобы обнаружить, когда процесс перестал писать к нему.
Я сделал что -то подобное только для мониторинга файлов журналов на консоли (в отличие от обработки), но принципы одинаковы. Как и вы, я использую файловую сторону, и важная логика находится в моем обработчике событий Onchanged:
case WatcherChangeTypes.Changed:
{
System.IO.FileInfo fi = new FileInfo(e.FullPath);
long prevLength;
if (lastPositions.TryGetValue(e.FullPath, out prevLength))
{
using (System.IO.FileStream fs = new FileStream(
e.FullPath, FileMode.Open, FileAccess.Read))
{
fs.Seek(prevLength, SeekOrigin.Begin);
DumpNewData(fs, (int)(fi.Length - prevLength));
lastPositions[e.FullPath] = fs.Position;
}
}
else
lastPositions.Add(e.FullPath, fi.Length);
break;
}
где есть последняя позиция
Dictionary<string, Int64> lastPositions = new Dictionary<string, long>();
И Sumpnewdata просто
private static void DumpNewData(FileStream fs, int bytesToRead)
{
byte[] bytesRead = new byte[bytesToRead];
fs.Read(bytesRead, 0, bytesToRead);
string s = System.Text.ASCIIEncoding.ASCII.GetString(bytesRead);
Console.Write(s);
}