Logger.cs 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. using UnityEngine;
  2. using System;
  3. using System.Collections;
  4. using System.Collections.Generic;
  5. using System.Diagnostics;
  6. using System.Text;
  7. /// <summary>
  8. /// This is a simple logger override, that will catch any AVDebug.LogXXX calls. It also exposes some simple wrappers
  9. /// for logging events in Flurry.
  10. /// To use it simply drag the Logger prefa into your first scene, and will continue to exist across scenes.
  11. ///
  12. /// It keeps track of the logs in a StringBuilder, which can then be viewed through Show Log button in
  13. /// the Debug Menu, through DebugViewManager and build must have DEBUG set in Scripting Define Symbols.
  14. /// The player can also Copy Log (in Web Build only, since we don't have access to clipboard in iOS or Android)
  15. ///
  16. /// It also keeps track of the last exception, which can be emailed through Email Last Exception button in
  17. /// the Debug Menu, through DebugViewManager and build must have DEBUG set in Scripting Define Symbols
  18. ///
  19. /// You can also halt the application on the first exception that occurs and show the Crash scene where they
  20. /// have access to the Debug Menu. DEBUG;HALT_ON_EXCEPTION must be set in Scripting Define Symbols.
  21. /// On iOS, Script Call Optimization must be set to Slow and Safe (otherwise it will crash the application)
  22. /// We only should use this when we have an issue with some client and want to get the stack trace of their
  23. /// crash
  24. ///
  25. /// ALWAYS MAKE SURE THAT ON RELEASE, TO HAVE Scripting Define Symbols EMPTY AND ON iOS TO HAVE Script Call Optimization
  26. /// SET TO FAST & FURIOUS.
  27. /// </summary>
  28. public class Logger : MonoBehaviour
  29. {
  30. #region Unity Singleton
  31. private static Logger _instance;
  32. public static Logger Instance
  33. {
  34. get
  35. {
  36. if (_instance == null)
  37. {
  38. _instance = FindObjectOfType(typeof(Logger)) as Logger;
  39. if (_instance == null)
  40. {
  41. AVDebug.LogError(string.Format("No gameObject with {0} component exists. Make sure to create a gameObject with {0} component", new System.Diagnostics.StackFrame().GetMethod().DeclaringType));
  42. }
  43. }
  44. return _instance;
  45. }
  46. }
  47. void Awake()
  48. {
  49. if (_instance != null && _instance != this)
  50. {
  51. AVDebug.LogWarning(string.Format("{0} Instance already exists on another gameObject. Destroying this gameObject {1}", this.GetType().Name, gameObject.name));
  52. Destroy(gameObject);
  53. return;
  54. }
  55. _instance = this;
  56. DontDestroyOnLoad(gameObject);
  57. }
  58. #endregion
  59. static StringBuilder log = new StringBuilder();
  60. Vector2 _logScrollPosition;
  61. bool _isShowingLog;
  62. public GUIStyle logBackgroundStyle;
  63. string _lastException;
  64. const int MAX_URL_SIZE = 2083; //According to http://stackoverflow.com/questions/4067391/getting-around-mailto-href-url-character-limit
  65. #region Unity Callbacks
  66. void OnEnable()
  67. {
  68. Application.logMessageReceived+=(LogCallbackHandler);
  69. DebugViewManager.OnDebugView += OnDebugView;
  70. }
  71. void OnDisable()
  72. {
  73. Application.logMessageReceived += (null);
  74. DebugViewManager.OnDebugView -= OnDebugView;
  75. }
  76. #endregion
  77. void LogCallbackHandler(string logString, string stackTrace, LogType type)
  78. {
  79. StackTrace s = new StackTrace(true);
  80. string logMessage = type + ": " + logString + "\n" + s.ToString();
  81. Log(logMessage);
  82. //Crash reporting based on this http://entitycrisis.blogspot.com/2011/01/error-reporting-from-your-unity3d-game.html
  83. if (type == LogType.Exception &&
  84. !logString.Contains("SocketException")) // Don't make it stop cause of a socket exception (as this would not allow to play offline)
  85. {
  86. _lastException = logMessage;
  87. #if HALT_ON_EXCEPTION
  88. Application.LoadLevel("Crash");
  89. #endif
  90. }
  91. }
  92. [System.Diagnostics.Conditional("DEBUG")]
  93. private static void Log(string message)
  94. {
  95. log.AppendLine(message);
  96. log.AppendLine();
  97. #if UNITY_WEBPLAYER
  98. Application.ExternalCall("LogMessage",message);
  99. #endif
  100. }
  101. public static void LogFlurryEvent(string eventName, params string[] keyValues)
  102. {
  103. #if UNITY_IPHONE || UNITY_ANDROID
  104. AVDebug.Assert(keyValues.Length % 2 == 0, "Make sure to pass key value pairs");
  105. var parameters = new Dictionary<string, string>();
  106. for (int i = 0; i < keyValues.Length; i += 2)
  107. {
  108. parameters[keyValues[i]] = keyValues[i + 1];
  109. }
  110. #if UNITY_IPHONE
  111. FlurryLogger.Instance.LogFlurryEvent(eventName, keyValues);
  112. #elif UNITY_ANDROID && !UNITY_EDITOR
  113. FlurryLogger.Instance.LogFlurryEvent(eventName, keyValues);
  114. //FlurryAndroid.logEvent(eventName, parameters, false);
  115. #endif
  116. #elif UNITY_WEBPLAYER
  117. Application.ExternalCall("LogFlurryEventWithParameters", eventName, keyValues);
  118. #endif
  119. //#if UNITY_IPHONE
  120. // FlurryBinding.logEvent(eventName, false);
  121. //#elif UNITY_ANDROID
  122. // FlurryLogger.Instance.LogFlurryEvent(eventName);
  123. // #elif UNITY_WEBPLAYER
  124. // Application.ExternalCall("LogFlurryEvent", eventName);
  125. // #endif
  126. }
  127. /*public static void LogFlurryEvent(string eventName, params string[] keyValues)
  128. {
  129. #if UNITY_IPHONE || UNITY_ANDROID
  130. AVDebug.Assert(keyValues.Length % 2 == 0, "Make sure to pass key value pairs");
  131. var parameters = new Dictionary<string, string>();
  132. for (int i = 0; i < keyValues.Length; i += 2)
  133. {
  134. parameters[keyValues[i]] = keyValues[i+1];
  135. }
  136. #if UNITY_IPHONE
  137. FlurryBinding.logEventWithParameters(eventName, parameters, false);
  138. #elif UNITY_ANDROID
  139. FlurryAndroid.logEvent(eventName, parameters, false);
  140. #endif
  141. #elif UNITY_WEBPLAYER
  142. Application.ExternalCall("LogFlurryEventWithParameters", eventName, keyValues);
  143. #endif
  144. }*/
  145. void OnDebugView()
  146. {
  147. if (_isShowingLog)
  148. {
  149. if (GUILayout.Button("Hide Log"))
  150. {
  151. _isShowingLog = false;
  152. }
  153. _logScrollPosition = GUILayout.BeginScrollView(_logScrollPosition, logBackgroundStyle, GUILayout.Width(Screen.width), GUILayout.Height(400));
  154. GUILayout.Label(log.ToString(), GUILayout.Width(2048));
  155. GUILayout.EndScrollView();
  156. }
  157. else
  158. {
  159. if (GUILayout.Button("Show Log"))
  160. {
  161. _isShowingLog = true;
  162. }
  163. }
  164. GUILayout.BeginHorizontal();
  165. if (GUILayout.Button("Clear Log"))
  166. {
  167. log = new StringBuilder();
  168. }
  169. #if UNITY_WEBPLAYER
  170. if (GUILayout.Button("Copy Log"))
  171. {
  172. //from http://forum.unity3d.com/threads/24101-Copy-TextField-or-TextArea-text-to-Clipboard
  173. TextEditor te = new TextEditor();
  174. te.content = new GUIContent(log.ToString());
  175. te.SelectAll();
  176. te.Copy();
  177. }
  178. #endif
  179. if (_lastException != null &&
  180. GUILayout.Button("Email Last Exception"))
  181. {
  182. string emailURL = "mailto:"+GameConstants.SUPPORT_EMAIL_FOR_IOS_CRASH_LOG+"?subject="+WWW.EscapeURL(GameConstants.GAME_NAME)+"%20log&body="+WWW.EscapeURL(_lastException);
  183. if (emailURL.Length > MAX_URL_SIZE) //which seems to be the limit of IE
  184. {
  185. emailURL.Substring(0, MAX_URL_SIZE);
  186. }
  187. Application.OpenURL(emailURL);
  188. }
  189. GUILayout.EndHorizontal();
  190. }
  191. }