Использование MemoryMappedFile и FileSystemWatcher для обнаружения новых записей в файле журнала

У меня есть файл журнала, написанный сторонним приложением, и я хотел бы, чтобы мое приложение «читало» этот файл журнала в режиме реального времени/в близком времени, анализировало новые записи журнала и действовало при определенных событиях.

Я думал, что смогу добиться этого с помощью комбинации 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);

person TJF    schedule 16.11.2010    source источник
comment
Я прокомментировал обязательный аргумент FileShare в сообщении Джеймса. MMF не может обойти полностью заблокированный файл, в этом случае вы застрянете.   -  person Hans Passant    schedule 17.11.2010
comment
Пожалуйста, смотрите мое редактирование выше. Похоже, что сейчас проблема больше сосредоточена на обходе монопольной блокировки, чем на MMF и FileStream. Я пробовал несколько оконных хвостовых инструментов, и все они, кажется, могут прекрасно обойти эксклюзивную блокировку, есть идеи, как они этого достигают?   -  person TJF    schedule 17.11.2010


Ответы (6)


Чтобы ответить на мой собственный вопрос, хитрость в чтении заблокированного файла заключается в создании FileStream со следующими флагами доступа:

FileStream fileStream = new System.IO.FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Delete | FileShare.ReadWrite);

Теперь нужно либо выполнять опрос на основе интервалов, либо искать события изменения FileSystemWatcher для обнаружения изменений файлов.

person TJF    schedule 16.11.2010
comment
Вы вообще использовали MemoryMappedFile? Я ищу, чтобы реализовать то же самое. - person Devela; 08.11.2012

Я не уверен, помогут ли вам MemoryMappedFiles. Взгляните на FileStream:

var stream = new FileStream(path, FileMode.Open, FileShare.Read);
stream.Seek(offset, SeekOrigin.Begin);

Хотя, если стороннее приложение заблокировало файл исключительно, вы мало что можете с этим поделать...

person James Kovacs    schedule 16.11.2010
comment
ну, с FileStream я не могу читать файлы, которые заблокированы исключительно другим процессом, и я надеялся, что смогу добиться этого с помощью файлов с отображением памяти. Хвостовые инструменты Windows, такие как baretail (baremetalsoft.com/baretail/index.php), могут чтобы читать заблокированные файлы просто отлично, поэтому должен быть какой-то способ добиться этого - person TJF; 17.11.2010
comment
FileShare.Read не будет работать, приложение, которое записывает файл журнала, уже получило доступ на запись. Вы не можете отказаться от права, которое уже было приобретено. Предположительно также проблема ОП. Вы должны указать здесь ReadWrite. - person Hans Passant; 17.11.2010

[Начало 2-го РЕДАКТИРОВАТЬ]

Еще одна идея...

Если стороннее приложение использует структуру ведения журналов, такую ​​как NLog, log4net или System.Diagnostics, вы все равно можете написать свой собственный Target/Appender/TraceListener и направить сообщения куда-нибудь, чтобы вы могли их просмотреть (например, файл, который не открыт исключительно для другого процесса и т. д.).

Если ваше стороннее приложение использует фреймворк ведения журналов, мы, вероятно, уже слышали об этом ;-)

[Конец 2-го РЕДАКТИРОВАТЬ]

[Начать РЕДАКТИРОВАНИЕ]

Я думаю, что неправильно понял вопрос. Сначала это звучало так, как будто вы используете стороннюю библиотеку, в которой реализовано ведение журнала, и вы хотели выполнить этот анализ из программы, которая генерировала ведение журнала. Перечитав свой вопрос, похоже, вы хотите «прослушать» файл журнала из-за пределов приложения. Если это так, мой ответ, вероятно, не поможет вам. Извиняюсь.

[Конец РЕДАКТИРОВАТЬ]

Мне нечего предложить по поводу MemoryMappedFiles, но мне интересно, сможете ли вы достичь того, что вам нужно, написав собственный прослушиватель/цель/аппендер для сторонней системы ведения журнала?

Например, если вы используете NLog, вы можете написать пользовательскую цель и направить туда все ваши сообщения журнала (а также направить их на «настоящую» цель(и)). Таким образом, вы получаете взлом каждого сообщения журнала по мере его регистрации (так что на самом деле это происходит в реальном времени, а не в реальном времени). Вы можете сделать то же самое с log4net и System.Diagnostics.

Обратите внимание, что NLog даже имеет цель «MethodCall». Чтобы использовать его, вам нужно только написать статический метод с правильной подписью. Я не знаю, есть ли у log4net аналогичная концепция.

Кажется, что было бы проще обеспечить надежную работу, чем пытаться читать и анализировать файл журнала, поскольку он записывается сторонним программным обеспечением.

person wageoghe    schedule 16.11.2010

Если файл «используется», с этим ничего нельзя поделать. Он действительно "используется". MemoryMappedFiles предназначены либо для чтения больших объемов данных с диска, либо для обмена данными с другими программами. Это не поможет обойти ограничение «в использовании».

person Joel Lucsy    schedule 16.11.2010

Файлы с отображением памяти имеют те же ограничения, что и FileStream, с которым вы его инициализируете, убедитесь, что вы инициализируете свой файл с отображением памяти следующим образом.

var readerStream = новый FileStream(путь, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);

var mmf = MemoryMappedFile.CreateFromFile(readerStream, null, 0, MemoryMappedFileAccess.Read, null, HandleInheritability.None, false);

Если какой-то другой процесс полностью заблокировал его даже при записи, вам не повезло, не уверен, что есть способ обойти это. Возможно, используйте какой-нибудь таймер, чтобы определить, когда процесс перестал писать в него.

person Homde    schedule 16.11.2010

Я сделал что-то подобное только для мониторинга файлов журнала на консоли (в отличие от обработки), но принципы те же. Как и вы, я использую FileSystemWatcher, и важная логика находится в моем обработчике событий 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;
}

где lastPositions

Dictionary<string, Int64> lastPositions = new Dictionary<string, long>();

а DumpNewData просто

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);
}
person 500 - Internal Server Error    schedule 16.11.2010
comment
к сожалению, это не относится к чтению файлов с монопольной блокировкой. - person TJF; 17.11.2010