WPF:我应该在我的日志中使用 string ob StringBuilder

WPF: should i use string ob StringBuilder with my Log

所以我有 WPf 应用程序和 Log4Net 所以每次我想添加 log 我只是这样添加:

log4net.Info("bla bla");

另外我想添加记录器 form 所以我创建了另一个 form 并且从我的主 form 我这样打开它:

LoggerForm loggerForm = new LoggerForm();
loggerForm.Show();

并创建日志object:

public class LogEntry : PropertyChangedBase
{
    public string DateTime { get; set; }
    public int Index { get; set; }
    public string Source{ get; set; }
    public Level Level { get; set; }        
    public string Message { get; set; }
}

LogHelper 将此 LogEvent objects 保存在 List 中,并将每个 LogEvent 添加到此 List 中:

public static class LogHelper
{
    public static ObservableCollection<LogEntry> LogEntries { get; set; }
    public static bool AddLogToList { get; set; }

    private static int Index;

    public static void AddLog(Level level, string message, string source)
    {
        if (AddLogToList)
        {
            Application.Current.Dispatcher.Invoke(new Action(() =>
            {
                if (LogEntries.Count == 1000)
                    LogEntries.RemoveAt(0);

                LogEntry logEntry = new LogEntry()
                {
                    DateTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss,fff"),
                    Index = Index++,
                    Level = level,
                    Source = source,
                    Message = message.Trim()
                };

                LogEntries.Add(logEntry);
            }));
        }
    }
}

然后从我的记录器 form InitializeComponent 注册到我的列表后 CollectionChanged:

LogHelper.AddLogToList = true;
LogHelper.LogEntries.CollectionChanged += LogEntries_CollectionChanged;

这一行:

LogHelper.AddLogToList = true;

表明我的记录器 form 已打开,因此我可以将我的 LogEvent 插入我的列表。

收藏已更改:

每次将新 LogEvent 添加到我的 List 时,我都会将我的 ItemSource 更新到我的 ListView:

ListView lvLogger;

private void LogEntries_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
    lvLogger.ItemsSource = LogHelper.LogEntries;
}

好的,这些是我的问题:

  1. 每次我想创建新的Log我输入两次:

    log.Info("bla bla");
    LogHelper.AddLog(Level.Info, "bla bla", $"MyClassName\MyMethodName"); 
    

    所以你可以看到我在这里使用了两次字符串,所以我想知道使用 String.Builder 代替 string 是否更好?

我还想提一下,我通过不同的线程更新我的日志。

  1. 当我的 Logger form 关闭时,我注册到它的关闭 event 并清除我的 LogEvent 列表:

    private void MetroWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
    {
        LogHelper.AddLogToList = false;
        LogHelper.LogEntries.Clear();
    }
    

    所以我的问题是我是否应该在这里注销我的 LogEntries_CollectionChanged 活动:

    LogHelper.LogEntries.CollectionChanged -= LogEntries_CollectionChanged;

或者这是多余的?

1) Stringbuilder 只有在您多次重复附加文本时才值得使用。除非那是你正在做的,否则我只会使用一个字符串。 如果这是您正在做的事情,那么也许您不应该这样做。

看看你的代码,你似乎已经有了一个消息变量,所以我可能对这部分关于使用它两次的内容感到有点困惑。也许你不是在谈论消息。 我会将一个字符串传递给一个方法,并在你的两行代码中使用该变量。假设他们使用相同的方法。

log.Info(message);
LogHelper.AddLog(Level.Info, message, $"MyClassName\MyMethodName");

2) 这部分我看了好几遍,感觉很混乱。不过,作为一般原则,如果您从非私有成员订阅事件,那么您应该取消订阅该事件以避免任何内存泄漏。因此,如果 collection 由不同的 window 或其他东西更新,那么您应该取消订阅该处理程序,这样 window 的实例可以在您不再需要它时被垃圾收集.

根据评论中的进一步解释,很明显您可以按递减的日期时间对 collection 进行排序,然后在顶部查看最新的日志条目。这将避免 collection 完全更改事件处理程序。 collection视图排序: https://social.technet.microsoft.com/wiki/contents/articles/26673.wpf-collectionview-tips.aspx#Sorting

经过更多解释... 似乎文本文件是用于显示的。 我认为你应该完全忘记文本文件并使用 observablecollection 作为循环列表。

您正在对日志调用 add 方法:

LogEntries.Add(logEntry);

所做的是附加到 collection。您可以改为使用 insert.

在特定索引处插入
LogEntries.Insert(0, logEntry);

添加到 collection 的 "top"。 我认为这也可以避免排序。 你不希望 collection 变得很大,所以一旦你达到一个数字(比如 100 ),你就可以删除最旧的。

LogEntries.RemoveItem(100);

记得先检查你有超过 100 个条目,否则会出错。

您可能需要考虑对任何其他问题进行更全面的解释。我们只知道您告诉我们的内容,人们不太可能坐在那里思考您正在做的事情足够长的时间来意识到... "hey that's all the logs and the file could be quite big"。

使用StringBuilder不会让你的代码变少。如果您想要更少的代码,请将 log4net.Info("bla bla"); 放在 LogHelper 中,或者创建同时执行这两项工作的东西。

StringBuilder 在这里不相关,因为 AndyStringbuilder is only worth using if you're repeatedly appending text numerous times.

更多信息 -> String vs Stringbuilder