Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
LoggerViewModel.cs
Go to the documentation of this file.
1 // Copyright (c) 2014 Silicon Studio Corp. (http://siliconstudio.co.jp)
2 // This file is distributed under GPL v3. See LICENSE.md for details.
3 using System;
4 using System.Collections.Generic;
5 using System.Collections.Specialized;
6 using System.Linq;
7 using System.Threading.Tasks;
8 
9 using SiliconStudio.Core.Diagnostics;
10 using SiliconStudio.Presentation.Collections;
12 using SiliconStudio.Presentation.Services;
13 
14 namespace SiliconStudio.Presentation.ViewModel
15 {
16  /// <summary>
17  /// A view model that monitors messages from one or several loggers and update an observable collection of <see cref="ILogMessage"/> using the dispatcher.
18  /// The updates are grouped together after a customizable delay to prevent blocking the UI thread.
19  /// </summary>
20  public class LoggerViewModel : DispatcherViewModel, IDisposable
21  {
22  /// <summary>
23  /// The default delay to wait before updating the <see cref="Messages"/> collection, after a message has been received.
24  /// </summary>
25  public const int DefaultUpdateInterval = 300;
26 
27  protected readonly Dictionary<Logger, List<ILogMessage>> Loggers = new Dictionary<Logger, List<ILogMessage>>();
28  private readonly ObservableList<ILogMessage> messages = new ObservableList<ILogMessage>();
29  private readonly List<Tuple<Logger, ILogMessage>> pendingMessages = new List<Tuple<Logger, ILogMessage>>();
30 
31  private int updateInterval = DefaultUpdateInterval;
32  private bool updatePending;
33 
34  /// <summary>
35  /// Initializes a new instance of the <see cref="LoggerViewModel"/> class.
36  /// </summary>
37  /// <param name="serviceProvider">A service provider that can provide a <see cref="IDispatcherService"/> to use for this view model.</param>
39  : base(serviceProvider)
40  {
41  AddLoggerCommand = new AnonymousCommand<Logger>(serviceProvider, AddLogger);
42  RemoveLoggerCommand = new AnonymousCommand<Logger>(serviceProvider, RemoveLogger);
43  ClearLoggersCommand = new AnonymousCommand(serviceProvider, ClearLoggers);
44  ClearMessagesCommand = new AsyncCommand(serviceProvider, ClearMessages);
45  messages.CollectionChanged += MessagesCollectionChanged;
46  }
47 
48  /// <summary>
49  /// Initializes a new instance of the <see cref="LoggerViewModel"/> class with a single logger.
50  /// </summary>
51  /// <param name="serviceProvider">A service provider that can provide a <see cref="IDispatcherService"/> to use for this view model.</param>
52  /// <param name="logger">The <see cref="Logger"/> to monitor.</param>
53  public LoggerViewModel(IViewModelServiceProvider serviceProvider, Logger logger)
54  : this(serviceProvider)
55  {
56  if (logger == null) throw new ArgumentNullException("logger");
57  Loggers.Add(logger, new List<ILogMessage>());
58  logger.MessageLogged += MessageLogged;
59  }
60 
61  /// <summary>
62  /// Initializes a new instance of the <see cref="LoggerViewModel"/> class with multiple loggers.
63  /// </summary>
64  /// <param name="serviceProvider">A service provider that can provide a <see cref="IDispatcherService"/> to use for this view model.</param>
65  /// <param name="loggers">The collection of <see cref="Logger"/> to monitor.</param>
67  : this(serviceProvider)
68  {
69  if (loggers == null) throw new ArgumentNullException("loggers");
70  foreach (var logger in loggers)
71  {
72  Loggers.Add(logger, new List<ILogMessage>());
73  logger.MessageLogged += MessageLogged;
74  }
75  ClearMessagesCommand = new AsyncCommand(serviceProvider, ClearMessages);
76  }
77 
78  /// <inheritdoc/>
79  public virtual void Dispose()
80  {
81  ClearLoggers();
82  }
83 
84  /// <summary>
85  /// Gets the collection of messages currently contained in this view model.
86  /// </summary>
87  public IReadOnlyObservableCollection<ILogMessage> Messages { get { return messages; } }
88 
89  /// <summary>
90  /// Gets or sets the interval in milliseconds between updates of the <see cref="Messages"/> collection. When a message is logged into one of the loggers,
91  /// the view model will wait this interval before actually updating the message collection to catch other potential messages in a single shot.
92  /// </summary>
93  /// <remarks>The default value is equal to <see cref="DefaultUpdateInterval"/>.</remarks>
94  public int UpdateInterval { get { return updateInterval; } set { SetValue(ref updateInterval, value); } }
95 
96  /// <summary>
97  /// Gets whether the monitored logs have warnings.
98  /// </summary>
99  public bool HasWarnings { get; private set; }
100 
101  /// <summary>
102  /// Gets whether the monitored logs have errors.
103  /// </summary>
104  public bool HasErrors { get; private set; }
105 
106  /// <summary>
107  /// Gets the command that will add a logger to monitor.
108  /// </summary>
109  /// <remarks>An instance of <see cref="Logger"/> must be passed as parameter of this command.</remarks>
110  public ICommandBase AddLoggerCommand { get; protected set; }
111 
112  /// <summary>
113  /// Gets the command that will remove a logger from monitoring.
114  /// </summary>
115  public ICommandBase RemoveLoggerCommand { get; protected set; }
116 
117  /// <summary>
118  /// Gets the command that will remove all loggers from monitoring.
119  /// </summary>
120  public ICommandBase ClearLoggersCommand { get; protected set; }
121 
122  /// <summary>
123  /// Gets the command that will clear the <see cref="Messages"/> collection.
124  /// </summary>
125  public ICommandBase ClearMessagesCommand { get; private set; }
126 
127  /// <summary>
128  /// Adds a <see cref="Logger"/> to monitor.
129  /// </summary>
130  /// <param name="logger">The <see cref="Logger"/> to monitor.</param>
131  public virtual void AddLogger(Logger logger)
132  {
133  Loggers.Add(logger, new List<ILogMessage>());
134  logger.MessageLogged += MessageLogged;
135  }
136 
137  /// <summary>
138  /// Removes a <see cref="Logger"/> from monitoring.
139  /// </summary>
140  /// <param name="logger">The <see cref="Logger"/> to remove from monitoring.</param>
141  public virtual void RemoveLogger(Logger logger)
142  {
143  Loggers.Remove(logger);
144  logger.MessageLogged -= MessageLogged;
145  }
146 
147  /// <summary>
148  /// Removes all loggers from monitoring.
149  /// </summary>
150  public virtual void ClearLoggers()
151  {
152  foreach (var logger in Loggers)
153  {
154  logger.Key.MessageLogged -= MessageLogged;
155  }
156  Loggers.Clear();
157  }
158 
159  /// <summary>
160  /// Flushes the pending log messages to add them immediately in the view model.
161  /// </summary>
162  public void Flush()
163  {
164  // Temporary cut the update interval. We use the backing field directly to
165  // prevent triggering a PropertyChanged event.
166  var interval = updateInterval;
167  updateInterval = 0;
168  Dispatcher.Invoke(UpdateMessages);
169  updateInterval = interval;
170  }
171 
172  /// <summary>
173  /// Clears the <see cref="Messages"/> collection.
174  /// </summary>
175  public void ClearMessages()
176  {
177  messages.Clear();
178  foreach (var logger in Loggers)
179  {
180  logger.Value.Clear();
181  }
182  }
183 
184  /// <summary>
185  /// Removes messages that comes from the given logger from the <see cref="Messages"/> collection.
186  /// </summary>
187  public void ClearMessages(Logger logger)
188  {
189  List<ILogMessage> messagesToRemove;
190  if (Loggers.TryGetValue(logger, out messagesToRemove))
191  {
192  foreach (var messageToRemove in messagesToRemove)
193  {
194  messages.Remove(messageToRemove);
195  }
196  }
197  }
198 
199  /// <summary>
200  /// The callback of the <see cref="Logger.MessageLogged"/> event, used to monitor incoming messages.
201  /// </summary>
202  /// <param name="sender">The event sender.</param>
203  /// <param name="args">The event argument.</param>
204  private void MessageLogged(object sender, MessageLoggedEventArgs args)
205  {
206  lock (pendingMessages)
207  {
208  pendingMessages.Add(Tuple.Create((Logger)sender, args.Message));
209  if (!updatePending)
210  {
211  updatePending = true;
212  Dispatcher.InvokeAsync(UpdateMessages);
213  }
214  }
215  }
216 
217  /// <summary>
218  /// This methods waits the <see cref="UpdateInterval"/> delay and then updates the <see cref="Messages"/> collection by adding all pending messages.
219  /// </summary>
220  private async void UpdateMessages()
221  {
222  if (UpdateInterval >= 0)
223  {
224  await Task.Delay(UpdateInterval);
225  }
226  List<Tuple<Logger, ILogMessage>> messagesToAdd = null;
227  lock (pendingMessages)
228  {
229  if (pendingMessages.Count > 0)
230  {
231  messagesToAdd = pendingMessages.ToList();
232  pendingMessages.Clear();
233  }
234  updatePending = false;
235  }
236  if (messagesToAdd != null)
237  {
238  foreach (var messageToAdd in messagesToAdd)
239  {
240  messages.Add(messageToAdd.Item2);
241  List<ILogMessage> logger;
242  if (Loggers.TryGetValue(messageToAdd.Item1, out logger))
243  {
244  logger.Add(messageToAdd.Item2);
245  }
246  }
247  }
248  }
249 
250  /// <summary>
251  /// Raised when the messages collection is changed. Updates <see cref="HasWarnings"/> and <see cref="HasErrors"/> properties.
252  /// </summary>
253  private void MessagesCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
254  {
255  if (e.Action == NotifyCollectionChangedAction.Add)
256  {
257  if (e.NewItems != null)
258  {
259  foreach (ILogMessage newMessage in e.NewItems)
260  {
261  switch (newMessage.Type)
262  {
263  case LogMessageType.Warning:
264  HasWarnings = true;
265  break;
266  case LogMessageType.Error:
267  case LogMessageType.Fatal:
268  HasErrors = true;
269  break;
270  }
271  }
272  }
273  }
274  else
275  {
276  HasWarnings = messages.Any(x => x.Type == LogMessageType.Warning);
277  HasErrors = messages.Any(x => x.Type == LogMessageType.Error || x.Type == LogMessageType.Fatal);
278  }
279  }
280  }
281 }
LoggerViewModel(IViewModelServiceProvider serviceProvider, IEnumerable< Logger > loggers)
Initializes a new instance of the LoggerViewModel class with multiple loggers.
void ClearMessages()
Clears the Messages collection.
An implementation of CommandBase that route Execute calls to a given anonymous method.
An interface representing an implementation of ICommand with additional properties.
Definition: ICommandBase.cs:10
void ClearMessages(Logger logger)
Removes messages that comes from the given logger from the Messages collection.
virtual void RemoveLogger(Logger logger)
Removes a Logger from monitoring.
Base implementation for ILogger.
Definition: Logger.cs:10
A view model that monitors messages from one or several loggers and update an observable collection o...
Arguments of the Logger.MessageLogged event.
This abstract class is an implementation of ViewModelBase that uses a dispatcher to invoke OnProperty...
LoggerViewModel(IViewModelServiceProvider serviceProvider, Logger logger)
Initializes a new instance of the LoggerViewModel class with a single logger.
The base interface for log messages used by the logging infrastructure.
Definition: ILogMessage.cs:8
LogMessageType Type
Gets or sets the type of this message.
Definition: ILogMessage.cs:23
void Flush()
Flushes the pending log messages to add them immediately in the view model.
virtual void AddLogger(Logger logger)
Adds a Logger to monitor.
LoggerViewModel(IViewModelServiceProvider serviceProvider)
Initializes a new instance of the LoggerViewModel class.
virtual void ClearLoggers()
Removes all loggers from monitoring.
ILogMessage Message
Gets the message that has been logged.