2012-03-18 38 views
6

Tôi đang tạo một chương trình điều khiển máy chủ trò chơi. Một trong những chức năng mà tôi đang tạo ra là một màn hình logfile server trực tiếp.Cách giám sát Textfile và nội dung đầu ra liên tục trong một hộp văn bản

Có tệp nhật ký (một tệp văn bản đơn giản) được máy chủ cập nhật khi máy chủ chạy.

Làm cách nào để liên tục kiểm tra tệp nhật ký và xuất nội dung của nó trong RichTextBox?

Tôi đã thực hiện chức năng đơn giản này chỉ cần thử và lấy nội dung của nhật ký. Tất nhiên, nó sẽ chỉ nhận được hàng văn bản theo hàng và xuất nó vào hộp văn bản của tôi. Ngoài ra nó sẽ khóa chương trình miễn là vòng lặp chạy, vì vậy tôi biết nó vô dụng.

public void ReadLog() 
{ 
    using (StreamReader reader = new StreamReader("server.log")) 
    { 
    String line; 

    // Read and display lines from the file until the end of the file is reached. 
    while ((line = reader.ReadLine()) != null) 
    { 
     monitorTextBox.AppendText(line + "\n"); 
     CursorDown(); 
    } 
    } 
} 

Nhưng làm thế nào bạn sẽ giải quyết việc theo dõi trực tiếp đơn giản nhất có thể?

* EDIT *

Tôi đang sử dụng giải pháp Prescots. công cụ tuyệt vời.

Hiện tại tôi đang sử dụng trình tạo sstreamreader để đặt văn bản từ tệp vào hộp văn bản của mình. Tôi chạy vào vấn đề là, bất cứ khi nào tôi cố gắng truy cập bất kỳ điều khiển gui trong trình xử lý sự kiện của tôi, chương trình chỉ dừng lại mà không có lỗi hoặc cảnh báo.

Tôi phát hiện ra rằng nó có liên quan đến luồng. Tôi đã giải quyết như sau:

private void OnChanged(object source, FileSystemEventArgs e) 
{ 
    if (monitorTextField.InvokeRequired) 
    { 
     monitorTextField.Invoke((MethodInvoker)delegate { OnChanged(source, e); }); 
    } 
    else 
    { 
     StreamReader reader = new StreamReader("file.txt"); 

     monitorTextField.Text = ""; 
     monitorTextField.Text = reader.ReadToEnd(); 
     reader.Close(); 
     CursorDown(); 
    } 
} 

Vấn đề duy nhất của tôi là tệp tin.txt được máy chủ sử dụng để tôi không thể truy cập vì nó đang được sử dụng bởi một quy trình khác ". Tôi không thể kiểm soát quá trình đó .. vì vậy có lẽ tôi không may mắn.

Nhưng tệp có thể được mở trong notepad trong khi serevr đang chạy, do đó, bằng cách nào đó nó phải có thể. Có lẽ tôi có thể làm một bản sao tạm thời của tập tin khi nó cập nhật và đọc bản sao. Dunno ...

+0

sử dụng bộ hẹn giờ ... và kiểm tra mỗi 10 giây hoặc 5 giây ... – SolidSnake

Trả lời

13

Kiểm tra các lớp System.IO.FileSystemWatcher:

public static Watch() 
{ 
    var watch = new FileSystemWatcher(); 
    watch.Path = @"D:\tmp"; 
    watch.Filter = "file.txt"; 
    watch.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite; //more options 
    watch.Changed += new FileSystemEventHandler(OnChanged); 
    watch.EnableRaisingEvents = true; 
} 

/// Functions: 
private static void OnChanged(object source, FileSystemEventArgs e) 
{ 
    if(e.FullPath == @"D:\tmp\file.txt") 
    { 
     // do stuff 
    } 
} 

Edit: nếu bạn biết một số chi tiết về các tập tin, bạn có thể xử lý một cách efficent nhất để có được những dòng cuối cùng. Ví dụ, có thể khi bạn đọc các tập tin, bạn có thể quét sạch những gì bạn đã đọc, vì vậy thời gian tiếp theo nó được cập nhật, bạn chỉ cần lấy bất cứ điều gì là có và đầu ra. Có lẽ bạn biết một dòng được thêm vào một lúc, sau đó mã của bạn có thể ngay lập tức nhảy đến dòng cuối cùng của tệp. Vv

+0

Cảm ơn bạn! Điều đó sẽ giúp ích rất nhiều. Tôi rất mới này ... Ngay bây giờ tôi chạy vào vấn đề noob của nhận được "Phương pháp phải có một loại trở lại", điều đó không xảy ra nếu tôi làm cho Watch() lớp như thế này "công void Xem () ". Tôi có thể đang tạo ra nó ở một nơi không đúng cách. Tôi đang đặt nó trong "MYPROGRAM một phần công cộng của tôi: Mẫu" – Christoffer

+0

ah vâng xin lỗi, tôi đã viết rằng mã xung quanh siêu nhanh. Bạn có thể trích xuất mã người theo dõi và đặt nó bất cứ khi nào thích hợp trong mã của bạn, nó không phải nằm trong một hàm Watch() mà tôi có ở trên. – Prescott

+0

Cảm ơn bạn! Tôi chỉ cần thêm một số thông tin vào câu hỏi đầu tiên (mới đây .. Tôi có vi phạm bất kỳ quy tắc nào không?) – Christoffer

0

Thử thêm Bộ hẹn giờ và đặt Timer.Tick đặt thành Khoảng thời gian là 1 giây. Trên Timer.Tick bạn chạy hàm.

private void myTimer_Tick(object sender, EventArgs e) 
{ 
    ReadLog(); 
} 
1

Như @Prescott đề xuất, hãy sử dụng một số FileSystemWatcher. Và đảm bảo rằng bạn mở tệp bằng chế độ FileShare thích hợp (FileShare.ReadWrite có vẻ thích hợp), vì tệp có thể vẫn được máy chủ mở. Nếu bạn cố gắng mở tệp độc quyền trong khi nó vẫn được sử dụng bởi một tiến trình khác, thao tác mở sẽ thất bại.

Ngoài ra để đạt được một chút hiệu suất, bạn có thể nhớ vị trí cuối cùng mà bạn đã đọc tệp và chỉ đọc các phần mới.

+0

Tôi có thể làm điều đó nếu tôi sử dụng StreamReader không? – Christoffer

+0

Tôi nghĩ rằng để tìm kiếm một vị trí cụ thể, bạn sẽ phải truy cập trực tiếp vào 'FileStream' bên dưới (thông qua thuộc tính' Position'). Vì vậy, bạn tạo đầu tiên 'FileStream', đi đến đúng vị trí, xây dựng một' StreamReader' trên đầu luồng, đọc dữ liệu và sau đó lấy lại vị trí cuối cùng từ 'FileStream'. (Tôi đã không thử nó, tôi hy vọng nó hoạt động như mô tả ...) – MartinStettner

3

Mặc dù FileSystemWatcher sự là giải pháp đơn giản nhất mà tôi đã tìm thấy nó là không đáng tin cậy trong thực tế .. thường là một tập tin có thể được cập nhật với nội dung mới nhưng FileSystemWatcher không cháy, một sự kiện cho đến giây sau và thường không bao giờ.

Cách đáng tin cậy duy nhất tôi đã tìm thấy để tiếp cận điều này là kiểm tra các thay đổi đối với tệp một cách thường xuyên bằng cách sử dụng đối tượng System.Timers.Timer và kiểm tra kích thước tệp.

Tôi đã viết một lớp học nhỏ thể hiện này có sẵn ở đây:

https://gist.github.com/ant-fx/989dd86a1ace38a9ac58

Ví dụ cách dùng

var monitor = new LogFileMonitor("c:\temp\app.log", "\r\n"); 

monitor.OnLine += (s, e) => 
{ 
    // WARNING.. this will be a different thread... 
    Console.WriteLine(e.Line); 
}; 

monitor.Start(); 

Chỉ có thực sự bất lợi ở đây (ngoài một sự chậm trễ thực hiện nhẹ do kiểm tra kích thước tập tin) là bởi vì nó sử dụng System.Timers.Timer gọi lại đến từ một chủ đề khác.

Nếu bạn đang sử dụng ứng dụng Windows Forms hoặc WPF, bạn có thể dễ dàng sửa đổi lớp để chấp nhận SynchronizingObject để đảm bảo sự kiện trình xử lý sự kiện được gọi từ cùng một chuỗi.

0

Sử dụng câu trả lời này trên một bài đăng khác c# continuously read file.

Cái này khá hiệu quả và kiểm tra một lần mỗi giây nếu kích thước tệp đã thay đổi.

Bạn có thể chạy nó trên một chuỗi khác (hoặc chuyển đổi thành mã không đồng bộ), nhưng trong bất kỳ trường hợp nào bạn sẽ cần phải sửa đổi văn bản trở lại luồng chính để nối thêm vào hộp văn bản.

Các vấn đề liên quan