-1

So i have WPf application and with Log4Netso each time i want to add log i just add it this way:

log4net.Info("bla bla");

In addition i want to add logger form so i created another form and from my main form i opens it this way:

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

And create Log 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; }
}

And LogHelper that hold this LogEvent objects inside List and also add every LogEvent into this 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);
            }));
        }
    }
}

And from my Logger form after InitializeComponent register into my list CollectionChanged:

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

This line:

LogHelper.AddLogToList = true;

Indicate that my Logger form is opened so i can insert my LogEvent iunto my List.

CollectionChanged:

Each time new LogEvent added into my List i update my ItemSource into my ListView:

ListView lvLogger;

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

Ok so those are my questions:

  1. Each time i want to create new Log i type it twice:

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

    So as you can see i use here string twice, so i wonder if this will better to use maybe String.Builder instead of string ?

I also want to mention that i update my log via different threads.

  1. When my Logger form closed i register to its closing event and clear my LogEvent list:

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

    So my question here is should i unregistered here to my LogEntries_CollectionChanged event:

    LogHelper.LogEntries.CollectionChanged -= LogEntries_CollectionChanged;

Or this is redundant ?

ASh
  • 34,632
  • 9
  • 60
  • 82
Dim Mark
  • 73
  • 2
  • 8

2 Answers2

1

1) Stringbuilder is only worth using if you're repeatedly appending text numerous times. Unless that's what you're doing then I would just use a string. If it is what you're doing then maybe you shouldn't be.

Looking at your code, you already seem to have a message variable so I'm maybe a bit confused by what this part about using it twice. Maybe you're not talking about message though. I would pass in a string to a method and use that variable in both your lines of code there. Assuming they're in the same method.

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

2) I read this part several times and it's confusing. As a general principle though, if you're subscribing to an event from something which isn't a private member then you should unsubscribe to that event in order to obviate any memory leaks. So if the collection is newed up by a different window or something then you should unsubscribe that handler so the instance of that window can be garbage collected if you don't need it any more.

From further explanation in the comments, it's become apparent that you could probably just sort your collection in decreasing datetime and see the latest log entry at the top. This would obviate that collectionchanged event handler completely. Sorting of collectionview: https://social.technet.microsoft.com/wiki/contents/articles/26673.wpf-collectionview-tips.aspx#Sorting

After even more explanation... It seems the text file is for display. I think you should forget the text file entirely and use an observablecollection as a circular list.

You are calling the add method on logentries:

LogEntries.Add(logEntry);

What that does is appends to the collection. You can instead insert at a specific index using insert.

LogEntries.Insert(0, logEntry);

That adds to the "top" of the collection. Which I think would also obviate sorting. You don't want that collection to get huge, so once you hit a number ( say 100 ) then you can remove the oldest.

LogEntries.RemoveItem(100);

Remember to first check you have over a 100 entries or it'll error.

You might want to consider fuller explanation on any further questions. We only know what you tell us and people are unlikely to sit there thinking about what you're doing for long enough to realise... "hey that's all the logs and the file could be quite big".

Andy
  • 11,864
  • 2
  • 17
  • 20
  • I forgot to mention that log.Info (for example) is my txt file log and my LogHelper.AddLog is my ListView inside my logger form and this only if the user want to open this logger form and see it in more convenient way then see txt file – Dim Mark Mar 27 '18 at 08:45
  • Something like File.AppendText could add a new string to a file. Keeping the entire file in memory doesn't sound like a good plan. If I follow your meaning. – Andy Mar 27 '18 at 08:53
  • So what your suggestions ? just put my string into one variable and this string pass into my 2 log objects ? (BTW i keeping my list up to 1000 entries and start to delete the first one when my list is about with 1000 entries) – Dim Mark Mar 27 '18 at 09:04
  • I don't follow why you want a txt file as well as a log file but yes I would just pass the same parameter to two methods. – Andy Mar 27 '18 at 09:26
  • Because my txt file can hold many entries and i have up to 10MB file with growing file and up to 3 files, if i will put so many entries inside my LisyView it will blow my memory – Dim Mark Mar 27 '18 at 10:17
  • This text file is a bad idea. You have control of the data. Why don't you add the entries to an observablecollection. You can use index to reference items in these. Use .insertat(0) to write to the top and .removeat(100) to remove old ones when it gets to 100 entries. This way you're using the collection like a circular list and binding is easy. – Andy Mar 27 '18 at 12:15
  • I did not understand the idea, can you please elaborate ? (or with code example) – Dim Mark Mar 27 '18 at 12:16
  • Dont you think that in addition to this log list this is ok the have also txt file that hold many item ? for example if someones will tell me that 2 days ago the application crash so i can in this case open the log txt and search for this crash\error, with Log4Net i have up to 10MB txt file and up to 3 files until this start to write agin on the same file – Dim Mark Mar 27 '18 at 15:39
  • Aren't you logging to the windows log? These are searchable. – Andy Mar 27 '18 at 15:58
  • No, this is better ? – Dim Mark Mar 27 '18 at 16:38
  • The windows log is where you'd expect to see stuff logged and it comes with a bunch of built in functionality like a viewer and you can clear em off. It seems the obvious place to log stuff to IMO. – Andy Mar 27 '18 at 19:54
  • Ok thanks i will check it, any recommended tutorial for that ? – Dim Mark Mar 28 '18 at 02:47
  • I'm pretty sure writing to the windows log is just one of the logging options on log4net. I don't have a specific recommendation on tutorials, I'd have to search eg https://www.google.co.uk/search?q=csharp+query+windows+log&ie=&oe= – Andy Mar 28 '18 at 07:51
0

Using StringBuilder won't make your code any less. If you want less code, put log4net.Info("bla bla"); in LogHelper, or create something that does both jobs a the same time.

StringBuilder is not relevant here as Andy said Stringbuilder is only worth using if you're repeatedly appending text numerous times.

For more information -> String vs Stringbuilder

tetralobita
  • 453
  • 6
  • 16
  • My problem do to that (put they both in the same function) is that my log4net configure to show me also the class name and the method ([%logger/%method]) so if i put this in one function all the entries will be from the same location and not from the original location – Dim Mark Mar 27 '18 at 11:41
  • BTW because StringBuilder is only if repeatedly appending text numerous times - can it be better to create static StringBuilder and put all my log messages inside this object ? – Dim Mark Mar 27 '18 at 11:43
  • No, creating static `StringBuilder` wont make any better either, and to collect correct method names from wrapped class you can check here https://stackoverflow.com/questions/1149844/how-to-log-method-name-when-using-wrapper-class-with-log4net – tetralobita Mar 27 '18 at 12:10