Question

I have a program that displays lines from a log file.

They are parsed and put into a class called LogLine then displayed in a datagrid

Here is my filter function:

ICollectionView view = CollectionViewSource.GetDefaultView(this.LogView.ItemsSource);
bool traceChecked = this.TraceCheckbox.IsChecked.HasValue &&     this.TraceCheckbox.IsChecked.Value;
bool debugChecked = this.DebugCheckbox.IsChecked.HasValue && this.DebugCheckbox.IsChecked.Value;
bool infoChecked = this.InfoCheckbox.IsChecked.HasValue && this.InfoCheckbox.IsChecked.Value;
bool warnChecked = this.WarnCheckbox.IsChecked.HasValue && this.WarnCheckbox.IsChecked.Value;
bool errorChecked = this.ErrorCheckbox.IsChecked.HasValue && this.ErrorCheckbox.IsChecked.Value;
string filtertext = this.TextFilterBox.Text;
view.Filter = o =>
    {
        LogLine line = o as LogLine;
        return line != null
               && (((traceChecked && line.Trace) 
               || (debugChecked && line.Debug) 
               || (infoChecked && line.Info) 
               || (warnChecked && line.Warn) 
               || (errorChecked && line.Error))
               && line.Message.Contains(filtertext));
    };

This function is slow, already, taking close to 5 seconds on a log with 200000 lines.

What can be done to speed this up?

I implemented a real ViewModel per HighCore's suggestion. This is marginally faster, but it is still taking 5-6 seconds to go threw all the lines of the ObservableCollection

ICollectionView view = CollectionViewSource.GetDefaultView(this.LogView.ItemsSource);
LogViewModel lvm = (LogViewModel)this.DataContext;
view.Filter = o =>
    {
        LogLine line = o as LogLine;
        if (line == null || !line.Message.Contains(lvm.FilterText))
            {
                return false;
            }

            switch (line.LogLevel)
            {
                case LogViewModel.LogLevel.Trace:
                    return lvm.Trace;
                case LogViewModel.LogLevel.Debug:
                    return lvm.Debug;
                case LogViewModel.LogLevel.Info:
                    return lvm.Info;
                case LogViewModel.LogLevel.Warn:
                    return lvm.Warn;
                case LogViewModel.LogLevel.Error:
                    return lvm.Error;
                default:
                    return false;
            }
        };
Was it helpful?

Solution

I wrote a similar log viewer app but used a different approach.

I perform an initial parse into a class like your LogLine and store them in a list. Then as the user chooses various filter combinations, I use Linq to build an IEnumerable of filter matches which is bound to an items control (in my case a ListView). You could use an ObservableCollection and clear / populate with the same results if you prefer.

I just tested with a 31MB file (240k lines) and the results are displayed in under a second when changing the filter.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top