Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
ConsoleLogListener.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.Diagnostics;
5 using System.IO;
6 using System.Reflection;
7 using System.Runtime.InteropServices;
8 #if SILICONSTUDIO_PLATFORM_ANDROID
9 using Android.Util;
10 #endif
11 #if SILICONSTUDIO_PLATFORM_WINDOWS_DESKTOP
12 using Microsoft.Win32.SafeHandles;
13 #endif
14 
15 namespace SiliconStudio.Core.Diagnostics
16 {
17  /// <summary>
18  /// A <see cref="LogListener"/> implementation redirecting its output to the default OS console. If console is not supported message are output to <see cref="Debug"/>
19  /// </summary>
21  {
22  private bool isConsoleActive;
23 
24  /// <summary>
25  /// Gets or sets the minimum log level handled by this listener.
26  /// </summary>
27  /// <value>The minimum log level.</value>
28  public LogMessageType LogLevel { get; set; }
29 
30  /// <summary>
31  /// Gets or sets the log mode.
32  /// </summary>
33  /// <value>The log mode.</value>
34  public ConsoleLogMode LogMode { get; set; }
35 
36  protected override void OnLog(ILogMessage logMessage)
37  {
38  // filter logs with lower level
39 
40  // Always log when debugger is attached
41  if (!Debugger.IsAttached &&
42  (logMessage.Type < LogLevel || LogMode == ConsoleLogMode.None
43  || (!(LogMode == ConsoleLogMode.Auto && Platform.IsRunningDebugAssembly) && LogMode != ConsoleLogMode.Always)))
44  {
45  return;
46  }
47 
48  // Make sure the console is opened when the debugger is not attached
49  if (!Debugger.IsAttached)
50  {
51  EnsureConsole();
52  }
53 
54 #if SILICONSTUDIO_PLATFORM_ANDROID
55  const string appliName = "Paradox";
56  var exceptionMsg = GetExceptionText(logMessage);
57  var messageText = GetDefaultText(logMessage);
58  if (!string.IsNullOrEmpty(exceptionMsg))
59  messageText += exceptionMsg;
60 
61  // set the color depending on the message log level
62  switch (logMessage.Type)
63  {
64  case LogMessageType.Debug:
65  Log.Debug(appliName, messageText);
66  break;
67  case LogMessageType.Verbose:
68  Log.Verbose(appliName, messageText);
69  break;
70  case LogMessageType.Info:
71  Log.Info(appliName, messageText);
72  break;
73  case LogMessageType.Warning:
74  Log.Warn(appliName, messageText);
75  break;
76  case LogMessageType.Error:
77  case LogMessageType.Fatal:
78  Log.Error(appliName, messageText);
79  break;
80  }
81  return;
82 #else // SILICONSTUDIO_PLATFORM_ANDROID
83 
84  var exceptionMsg = GetExceptionText(logMessage);
85 
86 #if SILICONSTUDIO_PLATFORM_WINDOWS_DESKTOP
87  // save initial console color
88  ConsoleColor initialColor = Console.ForegroundColor;
89 
90  // set the color depending on the message log level
91  switch (logMessage.Type)
92  {
93  case LogMessageType.Debug:
94  Console.ForegroundColor = ConsoleColor.DarkGray;
95  break;
96  case LogMessageType.Verbose:
97  Console.ForegroundColor = ConsoleColor.Gray;
98  break;
99  case LogMessageType.Info:
100  Console.ForegroundColor = ConsoleColor.Green;
101  break;
102  case LogMessageType.Warning:
103  Console.ForegroundColor = ConsoleColor.Yellow;
104  break;
105  case LogMessageType.Error:
106  case LogMessageType.Fatal:
107  Console.ForegroundColor = ConsoleColor.Red;
108  break;
109  }
110 #endif
111  // By default output to debug logger
112  bool useDebugLogger = true;
113 
114 #if SILICONSTUDIO_PLATFORM_MONO_MOBILE || SILICONSTUDIO_PLATFORM_WINDOWS_DESKTOP
115 
116  useDebugLogger = System.Diagnostics.Debugger.IsAttached;
117 
118  // Log the actual message
119  Console.WriteLine(GetDefaultText(logMessage));
120  if (!string.IsNullOrEmpty(exceptionMsg))
121  {
122  Console.WriteLine(exceptionMsg);
123  }
124 #endif
125 
126  if (useDebugLogger)
127  {
128  // Log the actual message
129  System.Diagnostics.Debug.WriteLine(GetDefaultText(logMessage));
130  if (!string.IsNullOrEmpty(exceptionMsg))
131  {
132  System.Diagnostics.Debug.WriteLine(logMessage);
133  }
134  }
135 
136 #if SILICONSTUDIO_PLATFORM_WINDOWS_DESKTOP
137 
138  // revert console initial color
139  Console.ForegroundColor = initialColor;
140 #endif
141 #endif // !SILICONSTUDIO_PLATFORM_ANDROID
142  }
143 
144 #if SILICONSTUDIO_PLATFORM_WINDOWS_DESKTOP
145 
146  // TODO: MOVE THIS CODE OUT IN A SEPARATE CLASS
147 
148  private void EnsureConsole()
149  {
150  if (Debugger.IsAttached || isConsoleActive || Environment.OSVersion.Platform != PlatformID.Win32NT)
151  {
152  return;
153  }
154 
155  // try to attach to the parent console, if the program is run directly from a console
156  var attachedToConsole = AttachConsole(-1);
157  if (!attachedToConsole)
158  {
159  // Else open a new console
160  ShowConsole();
161  }
162 
163  isConsoleActive = true;
164  }
165 
166  public static void ShowConsole()
167  {
168  var handle = GetConsoleWindow();
169 
170  var outputRedirected = IsHandleRedirected((IntPtr)StdOutConsoleHandle);
171 
172  if (handle == IntPtr.Zero || outputRedirected)
173  {
174  Stream originalStream = null;
175  if (outputRedirected)
176  {
177  originalStream = Console.OpenStandardOutput();
178  }
179 
180  AllocConsole();
181 
182  Stream outputStream = Console.OpenStandardOutput();
183  if (originalStream != null)
184  {
185  outputStream = new DualStream(originalStream, outputStream);
186  }
187 
188  TextWriter writer = new StreamWriter(outputStream) { AutoFlush = true };
189  Console.SetOut(writer);
190  }
191  else
192  {
193  const int SW_SHOW = 5;
194  ShowWindow(handle, SW_SHOW);
195  }
196  }
197 
198  private class DualStream : Stream
199  {
200  private Stream stream1;
201  private Stream stream2;
202 
203  public DualStream(Stream stream1, Stream stream2)
204  {
205  this.stream1 = stream1;
206  this.stream2 = stream2;
207  }
208 
209  public override void Flush()
210  {
211  stream1.Flush();
212  stream2.Flush();
213  }
214 
215  public override long Seek(long offset, SeekOrigin origin)
216  {
217  throw new NotImplementedException();
218  }
219 
220  public override void SetLength(long value)
221  {
222  throw new NotImplementedException();
223  }
224 
225  public override int Read(byte[] buffer, int offset, int count)
226  {
227  throw new NotImplementedException();
228  }
229 
230  public override void Write(byte[] buffer, int offset, int count)
231  {
232  stream1.Write(buffer, offset, count);
233  stream2.Write(buffer, offset, count);
234  }
235 
236  public override bool CanRead
237  {
238  get
239  {
240  return false;
241  }
242  }
243 
244  public override bool CanSeek
245  {
246  get
247  {
248  return false;
249  }
250  }
251 
252  public override bool CanWrite
253  {
254  get
255  {
256  return true;
257  }
258  }
259 
260  public override long Length
261  {
262  get
263  {
264  throw new NotImplementedException();
265  }
266  }
267 
268  public override long Position { get; set; }
269  }
270 
271  public static void HideConsole()
272  {
273  var handle = GetConsoleWindow();
274  const int SW_HIDE = 0;
275  ShowWindow(handle, SW_HIDE);
276  }
277 
278  private const int StdOutConsoleHandle = -11;
279 
280  [DllImport("kernel32", SetLastError = true)]
281  private static extern bool AttachConsole(int dwProcessId);
282 
283  [DllImport("kernel32.dll", SetLastError = true)]
284  private static extern bool AllocConsole();
285 
286  [DllImport("kernel32.dll")]
287  private static extern IntPtr GetConsoleWindow();
288 
289  [DllImport("user32.dll")]
290  private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
291 
292  [DllImport("kernel32.dll")]
293  private static extern IntPtr GetStdHandle(uint nStdHandle);
294 
295  [DllImport("kernel32.dll")]
296  private static extern void SetStdHandle(uint nStdHandle, IntPtr handle);
297 
298  [DllImport("kernel32.dll")]
299  private static extern int GetFileType(SafeFileHandle handle);
300 
301  [DllImport("kernel32.dll", SetLastError = true)]
302  private static extern bool GetConsoleMode(IntPtr hConsoleHandle, out int mode);
303 
304  private static bool IsHandleRedirected(IntPtr ioHandle)
305  {
306  if ((GetFileType(new SafeFileHandle(ioHandle, false)) & 2) != 2)
307  {
308  return true;
309  }
310  else
311  {
312  int mode;
313  return !GetConsoleMode(ioHandle, out mode);
314  }
315  }
316 
317 #else
318  private void EnsureConsole()
319  {
320  }
321 #endif
322  }
323 }
ConsoleLogMode
Defines how the console is opened.
static readonly bool IsRunningDebugAssembly
Gets a value indicating whether the running assembly is a debug assembly.
Definition: Platform.cs:53
_In_ size_t count
Definition: DirectXTexP.h:174
LogMessageType
Type of a LogMessage.
A base class to implement a log listener
Definition: LogListener.cs:10
Platform specific queries and functions.
Definition: Platform.cs:15
override void OnLog(ILogMessage logMessage)
Called when a log occurred.
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
ConsoleColor
Colors used by ConsoleProgram.Color
A LogListener implementation redirecting its output to the default OS console. If console is not supp...