1080 lines
34 KiB
C#
1080 lines
34 KiB
C#
// ----------------------------------------
|
|
//
|
|
// BuglyAgent.cs
|
|
//
|
|
// Author:
|
|
// Yeelik, <bugly@tencent.com>
|
|
//
|
|
// Copyright (c) 2015 Bugly, Tencent. All rights reserved.
|
|
//
|
|
// ----------------------------------------
|
|
//
|
|
using UnityEngine;
|
|
|
|
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Reflection;
|
|
using System.Text;
|
|
using System.Text.RegularExpressions;
|
|
|
|
using System.Runtime.InteropServices;
|
|
|
|
// We dont use the LogType enum in Unity as the numerical order doesnt suit our purposes
|
|
/// <summary>
|
|
/// Log severity.
|
|
/// { Log, LogDebug, LogInfo, LogWarning, LogAssert, LogError, LogException }
|
|
/// </summary>
|
|
public enum LogSeverity
|
|
{
|
|
Log,
|
|
LogDebug,
|
|
LogInfo,
|
|
LogWarning,
|
|
LogAssert,
|
|
LogError,
|
|
LogException
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bugly agent.
|
|
/// </summary>
|
|
public sealed class BuglyAgent
|
|
{
|
|
|
|
// Define delegate support multicasting to replace the 'Application.LogCallback'
|
|
public delegate void LogCallbackDelegate (string condition,string stackTrace,LogType type);
|
|
|
|
/// <summary>
|
|
/// Configs the type of the crash reporter and customized log level to upload
|
|
/// </summary>
|
|
/// <param name="type">Type. Default=0, 1=Bugly v2.x MSDK=2</param>
|
|
/// <param name="logLevel">Log level. Off=0,Error=1,Warn=2,Info=3,Debug=4</param>
|
|
public static void ConfigCrashReporter(int type, int logLevel){
|
|
_SetCrashReporterType (type);
|
|
_SetCrashReporterLogLevel (logLevel);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Init sdk with the specified appId.
|
|
/// <para>This will initialize sdk to report native exception such as obj-c, c/c++, java exceptions, and also enable c# exception handler to report c# exception logs</para>
|
|
/// </summary>
|
|
/// <param name="appId">App identifier.</param>
|
|
public static void InitWithAppId (string appId)
|
|
{
|
|
if (IsInitialized) {
|
|
DebugLog (null, "BuglyAgent has already been initialized.");
|
|
|
|
return;
|
|
}
|
|
|
|
if (string.IsNullOrEmpty (appId)) {
|
|
return;
|
|
}
|
|
|
|
// init the sdk with app id
|
|
InitBuglyAgent (appId);
|
|
DebugLog (null, "Initialized with app id: {0}", appId);
|
|
|
|
// Register the LogCallbackHandler by Application.RegisterLogCallback(Application.LogCallback)
|
|
_RegisterExceptionHandler ();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Only Enable the C# exception handler.
|
|
///
|
|
/// <para>
|
|
/// You can call it when you do not call the 'InitWithAppId(string)', but you must make sure initialized the sdk in elsewhere,
|
|
/// such as the native code in associated Android or iOS project.
|
|
/// </para>
|
|
///
|
|
/// <para>
|
|
/// Default Level is <c>LogError</c>, so the LogError, LogException will auto report.
|
|
/// </para>
|
|
///
|
|
/// <para>
|
|
/// You can call the method <code>BuglyAgent.ConfigAutoReportLogLevel(LogSeverity)</code>
|
|
/// to change the level to auto report if you known what are you doing.
|
|
/// </para>
|
|
///
|
|
/// </summary>
|
|
public static void EnableExceptionHandler ()
|
|
{
|
|
if (IsInitialized) {
|
|
DebugLog (null, "BuglyAgent has already been initialized.");
|
|
return;
|
|
}
|
|
|
|
DebugLog (null, "Only enable the exception handler, please make sure you has initialized the sdk in the native code in associated Android or iOS project.");
|
|
|
|
// Register the LogCallbackHandler by Application.RegisterLogCallback(Application.LogCallback)
|
|
_RegisterExceptionHandler ();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Registers the log callback handler.
|
|
///
|
|
/// If you need register logcallback using Application.RegisterLogCallback(LogCallback),
|
|
/// you can call this method to replace it.
|
|
///
|
|
/// <para></para>
|
|
/// </summary>
|
|
/// <param name="handler">Handler.</param>
|
|
public static void RegisterLogCallback (LogCallbackDelegate handler)
|
|
{
|
|
if (handler != null) {
|
|
DebugLog (null, "Add log callback handler: {0}", handler);
|
|
|
|
_LogCallbackEventHandler += handler;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the log callback extras handler.
|
|
/// </summary>
|
|
/// <param name="handler">Handler.</param>
|
|
public static void SetLogCallbackExtrasHandler(Func<Dictionary<string, string>> handler){
|
|
if (handler != null) {
|
|
_LogCallbackExtrasHandler = handler;
|
|
|
|
DebugLog(null, "Add log callback extra data handler : {0}", handler);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reports the exception.
|
|
/// </summary>
|
|
/// <param name="e">E.</param>
|
|
/// <param name="message">Message.</param>
|
|
public static void ReportException (System.Exception e, string message)
|
|
{
|
|
if (!IsInitialized) {
|
|
return;
|
|
}
|
|
|
|
DebugLog (null, "Report exception: {0}\n------------\n{1}\n------------", message, e);
|
|
|
|
_HandleException (e, message, false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reports the exception.
|
|
/// </summary>
|
|
/// <param name="name">Name.</param>
|
|
/// <param name="message">Message.</param>
|
|
/// <param name="stackTrace">Stack trace.</param>
|
|
public static void ReportException (string name, string message, string stackTrace)
|
|
{
|
|
if (!IsInitialized) {
|
|
return;
|
|
}
|
|
|
|
DebugLog (null, "Report exception: {0} {1} \n{2}", name, message, stackTrace);
|
|
|
|
_HandleException (LogSeverity.LogException, name, message, stackTrace, false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Unregisters the log callback.
|
|
/// </summary>
|
|
/// <param name="handler">Handler.</param>
|
|
public static void UnregisterLogCallback (LogCallbackDelegate handler)
|
|
{
|
|
if (handler != null) {
|
|
DebugLog (null, "Remove log callback handler");
|
|
|
|
_LogCallbackEventHandler -= handler;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the user identifier.
|
|
/// </summary>
|
|
/// <param name="userId">User identifier.</param>
|
|
public static void SetUserId (string userId)
|
|
{
|
|
if (!IsInitialized) {
|
|
return;
|
|
}
|
|
DebugLog (null, "Set user id: {0}", userId);
|
|
|
|
SetUserInfo (userId);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the scene.
|
|
/// </summary>
|
|
/// <param name="sceneId">Scene identifier.</param>
|
|
public static void SetScene (int sceneId)
|
|
{
|
|
if (!IsInitialized) {
|
|
return;
|
|
}
|
|
DebugLog (null, "Set scene: {0}", sceneId);
|
|
|
|
SetCurrentScene (sceneId);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds the scene data.
|
|
/// </summary>
|
|
/// <param name="key">Key.</param>
|
|
/// <param name="value">Value.</param>
|
|
public static void AddSceneData (string key, string value)
|
|
{
|
|
if (!IsInitialized) {
|
|
return;
|
|
}
|
|
|
|
DebugLog (null, "Add scene data: [{0}, {1}]", key, value);
|
|
|
|
AddKeyAndValueInScene (key, value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Configs the debug mode.
|
|
/// </summary>
|
|
/// <param name="enable">If set to <c>true</c> debug mode.</param>
|
|
public static void ConfigDebugMode (bool enable)
|
|
{
|
|
EnableDebugMode (enable);
|
|
DebugLog (null, "{0} the log message print to console", enable ? "Enable" : "Disable");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Configs the auto quit application.
|
|
/// </summary>
|
|
/// <param name="autoQuit">If set to <c>true</c> auto quit.</param>
|
|
public static void ConfigAutoQuitApplication (bool autoQuit)
|
|
{
|
|
_autoQuitApplicationAfterReport = autoQuit;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Configs the auto report log level. Default is LogSeverity.LogError.
|
|
/// <example>
|
|
/// LogSeverity { Log, LogDebug, LogInfo, LogWarning, LogAssert, LogError, LogException }
|
|
/// </example>
|
|
/// </summary>
|
|
///
|
|
/// <param name="level">Level.</param>
|
|
public static void ConfigAutoReportLogLevel (LogSeverity level)
|
|
{
|
|
_autoReportLogLevel = level;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Configs the default.
|
|
/// </summary>
|
|
/// <param name="channel">Channel.</param>
|
|
/// <param name="version">Version.</param>
|
|
/// <param name="user">User.</param>
|
|
/// <param name="delay">Delay.</param>
|
|
public static void ConfigDefault (string channel, string version, string user, long delay)
|
|
{
|
|
DebugLog (null, "Config default channel:{0}, version:{1}, user:{2}, delay:{3}", channel, version, user, delay);
|
|
ConfigDefaultBeforeInit (channel, version, user, delay);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Logs the debug.
|
|
/// </summary>
|
|
/// <param name="tag">Tag.</param>
|
|
/// <param name="format">Format.</param>
|
|
/// <param name="args">Arguments.</param>
|
|
public static void DebugLog (string tag, string format, params object[] args)
|
|
{
|
|
if(!_debugMode) {
|
|
return;
|
|
}
|
|
|
|
if (string.IsNullOrEmpty (format)) {
|
|
return;
|
|
}
|
|
|
|
Console.WriteLine ("[BuglyAgent] <Debug> - {0} : {1}", tag, string.Format (format, args));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Prints the log.
|
|
/// </summary>
|
|
/// <param name="level">Level.</param>
|
|
/// <param name="format">Format.</param>
|
|
/// <param name="args">Arguments.</param>
|
|
public static void PrintLog (LogSeverity level, string format, params object[] args)
|
|
{
|
|
if (string.IsNullOrEmpty (format)) {
|
|
return;
|
|
}
|
|
|
|
LogRecord (level, string.Format (format, args));
|
|
}
|
|
|
|
#if UNITY_EDITOR || UNITY_STANDALONE
|
|
|
|
#region Interface(Empty) in Editor
|
|
private static void InitBuglyAgent (string appId)
|
|
{
|
|
}
|
|
|
|
private static void ConfigDefaultBeforeInit(string channel, string version, string user, long delay){
|
|
}
|
|
|
|
private static void EnableDebugMode(bool enable){
|
|
}
|
|
|
|
private static void SetUserInfo(string userInfo){
|
|
}
|
|
|
|
private static void ReportException (int type,string name, string message, string stackTrace, bool quitProgram)
|
|
{
|
|
}
|
|
|
|
private static void SetCurrentScene(int sceneId) {
|
|
}
|
|
|
|
private static void AddKeyAndValueInScene(string key, string value){
|
|
}
|
|
|
|
private static void AddExtraDataWithException(string key, string value) {
|
|
// only impl for iOS
|
|
}
|
|
|
|
private static void LogRecord(LogSeverity level, string message){
|
|
}
|
|
|
|
private static void SetUnityVersion(){
|
|
|
|
}
|
|
#endregion
|
|
|
|
#elif UNITY_ANDROID
|
|
// #if UNITY_ANDROID
|
|
|
|
#region Interface for Android
|
|
private static readonly string GAME_AGENT_CLASS = "com.tencent.bugly.agent.GameAgent";
|
|
private static readonly int TYPE_U3D_CRASH = 4;
|
|
private static readonly int GAME_TYPE_UNITY = 2;
|
|
private static bool hasSetGameType = false;
|
|
private static AndroidJavaClass _gameAgentClass = null;
|
|
|
|
public static AndroidJavaClass GameAgent {
|
|
get {
|
|
if (_gameAgentClass == null) {
|
|
_gameAgentClass = new AndroidJavaClass(GAME_AGENT_CLASS);
|
|
// using (AndroidJavaClass clazz = new AndroidJavaClass(CLASS_UNITYAGENT)) {
|
|
// _gameAgentClass = clazz.CallStatic<AndroidJavaObject> ("getInstance");
|
|
// }
|
|
}
|
|
if (!hasSetGameType) {
|
|
// set game type: unity(2).
|
|
_gameAgentClass.CallStatic ("setGameType", GAME_TYPE_UNITY);
|
|
hasSetGameType = true;
|
|
}
|
|
return _gameAgentClass;
|
|
}
|
|
}
|
|
|
|
private static string _configChannel;
|
|
private static string _configVersion;
|
|
private static string _configUser;
|
|
private static long _configDelayTime;
|
|
|
|
private static void ConfigDefaultBeforeInit(string channel, string version, string user, long delay){
|
|
_configChannel = channel;
|
|
_configVersion = version;
|
|
_configUser = user;
|
|
_configDelayTime = delay;
|
|
}
|
|
|
|
private static bool _configCrashReporterPackage = false;
|
|
|
|
private static void ConfigCrashReporterPackage(){
|
|
|
|
if (!_configCrashReporterPackage) {
|
|
try {
|
|
GameAgent.CallStatic("setSdkPackageName", _crashReporterPackage);
|
|
_configCrashReporterPackage = true;
|
|
} catch {
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
private static void InitBuglyAgent(string appId)
|
|
{
|
|
if (IsInitialized) {
|
|
return;
|
|
}
|
|
|
|
ConfigCrashReporterPackage();
|
|
|
|
try {
|
|
GameAgent.CallStatic("initCrashReport", appId, _configChannel, _configVersion, _configUser, _configDelayTime);
|
|
_isInitialized = true;
|
|
} catch {
|
|
|
|
}
|
|
}
|
|
|
|
private static void EnableDebugMode(bool enable){
|
|
_debugMode = enable;
|
|
|
|
ConfigCrashReporterPackage();
|
|
|
|
try {
|
|
GameAgent.CallStatic("setLogEnable", enable);
|
|
} catch {
|
|
|
|
}
|
|
}
|
|
|
|
private static void SetUserInfo(string userInfo){
|
|
ConfigCrashReporterPackage();
|
|
|
|
try {
|
|
GameAgent.CallStatic("setUserId", userInfo);
|
|
} catch {
|
|
}
|
|
}
|
|
|
|
private static void ReportException (int type, string name, string reason, string stackTrace, bool quitProgram)
|
|
{
|
|
ConfigCrashReporterPackage();
|
|
|
|
try {
|
|
GameAgent.CallStatic("postException", TYPE_U3D_CRASH, name, reason, stackTrace, quitProgram);
|
|
} catch {
|
|
|
|
}
|
|
}
|
|
|
|
private static void SetCurrentScene(int sceneId) {
|
|
ConfigCrashReporterPackage();
|
|
|
|
try {
|
|
GameAgent.CallStatic("setUserSceneTag", sceneId);
|
|
} catch {
|
|
|
|
}
|
|
}
|
|
|
|
private static void SetUnityVersion(){
|
|
ConfigCrashReporterPackage();
|
|
|
|
try {
|
|
GameAgent.CallStatic("setSdkConfig", "UnityVersion", Application.unityVersion);
|
|
} catch {
|
|
|
|
}
|
|
}
|
|
|
|
private static void AddKeyAndValueInScene(string key, string value){
|
|
ConfigCrashReporterPackage();
|
|
|
|
try {
|
|
GameAgent.CallStatic("putUserData", key, value);
|
|
} catch {
|
|
|
|
}
|
|
}
|
|
|
|
private static void AddExtraDataWithException(string key, string value) {
|
|
// no impl
|
|
}
|
|
|
|
private static void LogRecord(LogSeverity level, string message){
|
|
if (level < LogSeverity.LogWarning) {
|
|
DebugLog (level.ToString (), message);
|
|
}
|
|
|
|
ConfigCrashReporterPackage();
|
|
|
|
try {
|
|
GameAgent.CallStatic("printLog", string.Format ("<{0}> - {1}", level.ToString (), message));
|
|
} catch {
|
|
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#elif UNITY_IPHONE || UNITY_IOS
|
|
|
|
#region Interface for iOS
|
|
|
|
private static bool _crashReporterTypeConfiged = false;
|
|
|
|
private static void ConfigCrashReporterType(){
|
|
if (!_crashReporterTypeConfiged) {
|
|
try {
|
|
_BuglyConfigCrashReporterType(_crashReporterType);
|
|
_crashReporterTypeConfiged = true;
|
|
} catch {
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void ConfigDefaultBeforeInit(string channel, string version, string user, long delay){
|
|
ConfigCrashReporterType();
|
|
|
|
try {
|
|
_BuglyDefaultConfig(channel, version, user, null);
|
|
} catch {
|
|
|
|
}
|
|
}
|
|
|
|
private static void EnableDebugMode(bool enable){
|
|
_debugMode = enable;
|
|
}
|
|
|
|
private static void InitBuglyAgent (string appId)
|
|
{
|
|
ConfigCrashReporterType();
|
|
|
|
if(!string.IsNullOrEmpty(appId)) {
|
|
|
|
_BuglyInit(appId, _debugMode, _crashReproterCustomizedLogLevel); // Log level
|
|
}
|
|
}
|
|
|
|
private static void SetUnityVersion(){
|
|
ConfigCrashReporterType();
|
|
|
|
_BuglySetExtraConfig("UnityVersion", Application.unityVersion);
|
|
}
|
|
|
|
private static void SetUserInfo(string userInfo){
|
|
if(!string.IsNullOrEmpty(userInfo)) {
|
|
ConfigCrashReporterType();
|
|
|
|
_BuglySetUserId(userInfo);
|
|
}
|
|
}
|
|
|
|
private static void ReportException (int type, string name, string reason, string stackTrace, bool quitProgram)
|
|
{
|
|
ConfigCrashReporterType();
|
|
|
|
string extraInfo = "";
|
|
Dictionary<string, string> extras = null;
|
|
if (_LogCallbackExtrasHandler != null) {
|
|
extras = _LogCallbackExtrasHandler();
|
|
}
|
|
if (extras == null || extras.Count == 0) {
|
|
extras = new Dictionary<string, string> ();
|
|
extras.Add ("UnityVersion", Application.unityVersion);
|
|
}
|
|
|
|
if (extras != null && extras.Count > 0) {
|
|
if (!extras.ContainsKey("UnityVersion")) {
|
|
extras.Add ("UnityVersion", Application.unityVersion);
|
|
}
|
|
|
|
StringBuilder builder = new StringBuilder();
|
|
foreach(KeyValuePair<string,string> kvp in extras){
|
|
builder.Append(string.Format("\"{0}\" : \"{1}\"", kvp.Key, kvp.Value)).Append(" , ");
|
|
}
|
|
extraInfo = string.Format("{{ {0} }}", builder.ToString().TrimEnd(" , ".ToCharArray()));
|
|
}
|
|
|
|
// 4 is C# exception
|
|
_BuglyReportException(4, name, reason, stackTrace, extraInfo, quitProgram);
|
|
}
|
|
|
|
private static void SetCurrentScene(int sceneId) {
|
|
ConfigCrashReporterType();
|
|
|
|
_BuglySetTag(sceneId);
|
|
}
|
|
|
|
private static void AddKeyAndValueInScene(string key, string value){
|
|
ConfigCrashReporterType();
|
|
|
|
_BuglySetKeyValue(key, value);
|
|
}
|
|
|
|
private static void AddExtraDataWithException(string key, string value) {
|
|
|
|
}
|
|
|
|
private static void LogRecord(LogSeverity level, string message){
|
|
if (level < LogSeverity.LogWarning) {
|
|
DebugLog (level.ToString (), message);
|
|
}
|
|
|
|
ConfigCrashReporterType();
|
|
|
|
_BuglyLogMessage(LogSeverityToInt(level), null, message);
|
|
}
|
|
|
|
private static int LogSeverityToInt(LogSeverity logLevel){
|
|
int level = 5;
|
|
switch(logLevel) {
|
|
case LogSeverity.Log:
|
|
level = 5;
|
|
break;
|
|
case LogSeverity.LogDebug:
|
|
level = 4;
|
|
break;
|
|
case LogSeverity.LogInfo:
|
|
level = 3;
|
|
break;
|
|
case LogSeverity.LogWarning:
|
|
case LogSeverity.LogAssert:
|
|
level = 2;
|
|
break;
|
|
case LogSeverity.LogError:
|
|
case LogSeverity.LogException:
|
|
level = 1;
|
|
break;
|
|
default:
|
|
level = 0;
|
|
break;
|
|
}
|
|
return level;
|
|
}
|
|
|
|
// --- dllimport start ---
|
|
[DllImport("__Internal")]
|
|
private static extern void _BuglyInit(string appId, bool debug, int level);
|
|
|
|
[DllImport("__Internal")]
|
|
private static extern void _BuglySetUserId(string userId);
|
|
|
|
[DllImport("__Internal")]
|
|
private static extern void _BuglySetTag(int tag);
|
|
|
|
[DllImport("__Internal")]
|
|
private static extern void _BuglySetKeyValue(string key, string value);
|
|
|
|
[DllImport("__Internal")]
|
|
private static extern void _BuglyReportException(int type, string name, string reason, string stackTrace, string extras, bool quit);
|
|
|
|
[DllImport("__Internal")]
|
|
private static extern void _BuglyDefaultConfig(string channel, string version, string user, string deviceId);
|
|
|
|
[DllImport("__Internal")]
|
|
private static extern void _BuglyLogMessage(int level, string tag, string log);
|
|
|
|
[DllImport("__Internal")]
|
|
private static extern void _BuglyConfigCrashReporterType(int type);
|
|
|
|
[DllImport("__Internal")]
|
|
private static extern void _BuglySetExtraConfig(string key, string value);
|
|
|
|
// dllimport end
|
|
#endregion
|
|
|
|
#endif
|
|
|
|
#region Privated Fields and Methods
|
|
private static event LogCallbackDelegate _LogCallbackEventHandler;
|
|
|
|
private static bool _isInitialized = false;
|
|
private static LogSeverity _autoReportLogLevel = LogSeverity.LogError;
|
|
|
|
private static int _crashReporterType = 1; // Default=0,1=Bugly-V2,MSDKBugly=2, IMSDKBugly=3
|
|
|
|
#if UNITY_ANDROID
|
|
// The crash reporter package name, default is 'com.tencent.bugly'
|
|
private static string _crashReporterPackage = "com.tencent.bugly";
|
|
#endif
|
|
#if UNITY_IPHONE || UNITY_IOS
|
|
private static int _crashReproterCustomizedLogLevel = 2; // Off=0,Error=1,Warn=2,Info=3,Debug=4
|
|
#endif
|
|
|
|
#pragma warning disable 414
|
|
private static bool _debugMode = false;
|
|
private static bool _autoQuitApplicationAfterReport = false;
|
|
|
|
private static readonly int EXCEPTION_TYPE_UNCAUGHT = 1;
|
|
private static readonly int EXCEPTION_TYPE_CAUGHT = 2;
|
|
private static readonly string _pluginVersion = "1.5.1";
|
|
|
|
private static Func<Dictionary<string, string>> _LogCallbackExtrasHandler;
|
|
|
|
public static string PluginVersion {
|
|
get { return _pluginVersion; }
|
|
}
|
|
|
|
public static bool IsInitialized {
|
|
get { return _isInitialized; }
|
|
}
|
|
|
|
public static bool AutoQuitApplicationAfterReport {
|
|
get { return _autoQuitApplicationAfterReport; }
|
|
}
|
|
|
|
private static void _SetCrashReporterType(int type){
|
|
_crashReporterType = type;
|
|
|
|
if (_crashReporterType == 2) {
|
|
#if UNITY_ANDROID
|
|
_crashReporterPackage = "com.tencent.bugly.msdk";
|
|
#endif
|
|
}
|
|
|
|
}
|
|
|
|
private static void _SetCrashReporterLogLevel(int logLevel){
|
|
#if UNITY_IPHONE || UNITY_IOS
|
|
_crashReproterCustomizedLogLevel = logLevel;
|
|
#endif
|
|
}
|
|
|
|
private static void _RegisterExceptionHandler ()
|
|
{
|
|
try {
|
|
// hold only one instance
|
|
|
|
#if UNITY_5
|
|
Application.logMessageReceived += _OnLogCallbackHandler;
|
|
#else
|
|
Application.RegisterLogCallback (_OnLogCallbackHandler);
|
|
#endif
|
|
AppDomain.CurrentDomain.UnhandledException += _OnUncaughtExceptionHandler;
|
|
|
|
_isInitialized = true;
|
|
|
|
DebugLog (null, "Register the log callback in Unity {0}", Application.unityVersion);
|
|
} catch {
|
|
|
|
}
|
|
|
|
SetUnityVersion ();
|
|
}
|
|
|
|
private static void _UnregisterExceptionHandler ()
|
|
{
|
|
try {
|
|
#if UNITY_5
|
|
Application.logMessageReceived -= _OnLogCallbackHandler;
|
|
#else
|
|
Application.RegisterLogCallback (null);
|
|
#endif
|
|
System.AppDomain.CurrentDomain.UnhandledException -= _OnUncaughtExceptionHandler;
|
|
DebugLog (null, "Unregister the log callback in unity {0}", Application.unityVersion);
|
|
} catch {
|
|
|
|
}
|
|
}
|
|
|
|
private static void _OnLogCallbackHandler (string condition, string stackTrace, LogType type)
|
|
{
|
|
if (_LogCallbackEventHandler != null) {
|
|
_LogCallbackEventHandler (condition, stackTrace, type);
|
|
}
|
|
|
|
if (!IsInitialized) {
|
|
return;
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty (condition) && condition.Contains ("[BuglyAgent] <Log>")) {
|
|
return;
|
|
}
|
|
|
|
if (_uncaughtAutoReportOnce) {
|
|
return;
|
|
}
|
|
|
|
// convert the log level
|
|
LogSeverity logLevel = LogSeverity.Log;
|
|
switch (type) {
|
|
case LogType.Exception:
|
|
logLevel = LogSeverity.LogException;
|
|
break;
|
|
case LogType.Error:
|
|
logLevel = LogSeverity.LogError;
|
|
break;
|
|
case LogType.Assert:
|
|
logLevel = LogSeverity.LogAssert;
|
|
break;
|
|
case LogType.Warning:
|
|
logLevel = LogSeverity.LogWarning;
|
|
break;
|
|
case LogType.Log:
|
|
logLevel = LogSeverity.LogDebug;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (LogSeverity.Log == logLevel) {
|
|
return;
|
|
}
|
|
|
|
_HandleException (logLevel, null, condition, stackTrace, true);
|
|
}
|
|
|
|
private static void _OnUncaughtExceptionHandler (object sender, System.UnhandledExceptionEventArgs args)
|
|
{
|
|
if (args == null || args.ExceptionObject == null) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
if (args.ExceptionObject.GetType () != typeof(System.Exception)) {
|
|
return;
|
|
}
|
|
} catch {
|
|
if (UnityEngine.Debug.isDebugBuild == true) {
|
|
UnityEngine.Debug.Log ("BuglyAgent: Failed to report uncaught exception");
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (!IsInitialized) {
|
|
return;
|
|
}
|
|
|
|
if (_uncaughtAutoReportOnce) {
|
|
return;
|
|
}
|
|
|
|
_HandleException ((System.Exception)args.ExceptionObject, null, true);
|
|
}
|
|
|
|
private static void _HandleException (System.Exception e, string message, bool uncaught)
|
|
{
|
|
if (e == null) {
|
|
return;
|
|
}
|
|
|
|
if (!IsInitialized) {
|
|
return;
|
|
}
|
|
|
|
string name = e.GetType ().Name;
|
|
string reason = e.Message;
|
|
|
|
if (!string.IsNullOrEmpty (message)) {
|
|
reason = string.Format ("{0}{1}***{2}", reason, Environment.NewLine, message);
|
|
}
|
|
|
|
StringBuilder stackTraceBuilder = new StringBuilder ("");
|
|
|
|
StackTrace stackTrace = new StackTrace (e, true);
|
|
int count = stackTrace.FrameCount;
|
|
for (int i = 0; i < count; i++) {
|
|
StackFrame frame = stackTrace.GetFrame (i);
|
|
|
|
stackTraceBuilder.AppendFormat ("{0}.{1}", frame.GetMethod ().DeclaringType.Name, frame.GetMethod ().Name);
|
|
|
|
ParameterInfo[] parameters = frame.GetMethod ().GetParameters ();
|
|
if (parameters == null || parameters.Length == 0) {
|
|
stackTraceBuilder.Append (" () ");
|
|
} else {
|
|
stackTraceBuilder.Append (" (");
|
|
|
|
int pcount = parameters.Length;
|
|
|
|
ParameterInfo param = null;
|
|
for (int p = 0; p < pcount; p++) {
|
|
param = parameters [p];
|
|
stackTraceBuilder.AppendFormat ("{0} {1}", param.ParameterType.Name, param.Name);
|
|
|
|
if (p != pcount - 1) {
|
|
stackTraceBuilder.Append (", ");
|
|
}
|
|
}
|
|
param = null;
|
|
|
|
stackTraceBuilder.Append (") ");
|
|
}
|
|
|
|
string fileName = frame.GetFileName ();
|
|
if (!string.IsNullOrEmpty (fileName) && !fileName.ToLower ().Equals ("unknown")) {
|
|
fileName = fileName.Replace ("\\", "/");
|
|
|
|
int loc = fileName.ToLower ().IndexOf ("/assets/");
|
|
if (loc < 0) {
|
|
loc = fileName.ToLower ().IndexOf ("assets/");
|
|
}
|
|
|
|
if (loc > 0) {
|
|
fileName = fileName.Substring (loc);
|
|
}
|
|
|
|
stackTraceBuilder.AppendFormat ("(at {0}:{1})", fileName, frame.GetFileLineNumber ());
|
|
}
|
|
stackTraceBuilder.AppendLine ();
|
|
}
|
|
|
|
// report
|
|
_reportException (uncaught, name, reason, stackTraceBuilder.ToString ());
|
|
}
|
|
|
|
private static void _reportException (bool uncaught, string name, string reason, string stackTrace)
|
|
{
|
|
if (string.IsNullOrEmpty (name)) {
|
|
return;
|
|
}
|
|
|
|
if (string.IsNullOrEmpty (stackTrace)) {
|
|
stackTrace = StackTraceUtility.ExtractStackTrace ();
|
|
}
|
|
|
|
if (string.IsNullOrEmpty (stackTrace)) {
|
|
stackTrace = "Empty";
|
|
} else {
|
|
|
|
try {
|
|
string[] frames = stackTrace.Split ('\n');
|
|
|
|
if (frames != null && frames.Length > 0) {
|
|
|
|
StringBuilder trimFrameBuilder = new StringBuilder ();
|
|
|
|
string frame = null;
|
|
int count = frames.Length;
|
|
for (int i = 0; i < count; i++) {
|
|
frame = frames [i];
|
|
|
|
if (string.IsNullOrEmpty (frame) || string.IsNullOrEmpty (frame.Trim ())) {
|
|
continue;
|
|
}
|
|
|
|
frame = frame.Trim ();
|
|
|
|
// System.Collections.Generic
|
|
if (frame.StartsWith ("System.Collections.Generic.") || frame.StartsWith ("ShimEnumerator")) {
|
|
continue;
|
|
}
|
|
if (frame.StartsWith ("Bugly")) {
|
|
continue;
|
|
}
|
|
if (frame.Contains ("..ctor")) {
|
|
continue;
|
|
}
|
|
|
|
int start = frame.ToLower ().IndexOf ("(at");
|
|
int end = frame.ToLower ().IndexOf ("/assets/");
|
|
|
|
if (start > 0 && end > 0) {
|
|
trimFrameBuilder.AppendFormat ("{0}(at {1}", frame.Substring (0, start).Replace (":", "."), frame.Substring (end));
|
|
} else {
|
|
trimFrameBuilder.Append (frame.Replace (":", "."));
|
|
}
|
|
|
|
trimFrameBuilder.AppendLine ();
|
|
}
|
|
|
|
stackTrace = trimFrameBuilder.ToString ();
|
|
}
|
|
} catch {
|
|
PrintLog(LogSeverity.LogWarning,"{0}", "Error to parse the stack trace");
|
|
}
|
|
|
|
}
|
|
|
|
PrintLog (LogSeverity.LogError, "ReportException: {0} {1}\n*********\n{2}\n*********", name, reason, stackTrace);
|
|
|
|
_uncaughtAutoReportOnce = uncaught && _autoQuitApplicationAfterReport;
|
|
|
|
ReportException (uncaught ? EXCEPTION_TYPE_UNCAUGHT : EXCEPTION_TYPE_CAUGHT, name, reason, stackTrace, uncaught && _autoQuitApplicationAfterReport);
|
|
}
|
|
|
|
private static void _HandleException (LogSeverity logLevel, string name, string message, string stackTrace, bool uncaught)
|
|
{
|
|
if (!IsInitialized) {
|
|
DebugLog (null, "It has not been initialized.");
|
|
return;
|
|
}
|
|
|
|
if (logLevel == LogSeverity.Log) {
|
|
return;
|
|
}
|
|
|
|
if ((uncaught && logLevel < _autoReportLogLevel)) {
|
|
DebugLog (null, "Not report exception for level {0}", logLevel.ToString ());
|
|
return;
|
|
}
|
|
|
|
string type = null;
|
|
string reason = null;
|
|
|
|
if (!string.IsNullOrEmpty (message)) {
|
|
try {
|
|
if ((LogSeverity.LogException == logLevel) && message.Contains ("Exception")) {
|
|
|
|
Match match = new Regex (@"^(?<errorType>\S+):\s*(?<errorMessage>.*)", RegexOptions.Singleline).Match (message);
|
|
|
|
if (match.Success) {
|
|
type = match.Groups ["errorType"].Value.Trim();
|
|
reason = match.Groups ["errorMessage"].Value.Trim ();
|
|
}
|
|
} else if ((LogSeverity.LogError == logLevel) && message.StartsWith ("Unhandled Exception:")) {
|
|
|
|
Match match = new Regex (@"^Unhandled\s+Exception:\s*(?<exceptionName>\S+):\s*(?<exceptionDetail>.*)", RegexOptions.Singleline).Match(message);
|
|
|
|
if (match.Success) {
|
|
string exceptionName = match.Groups ["exceptionName"].Value.Trim();
|
|
string exceptionDetail = match.Groups ["exceptionDetail"].Value.Trim ();
|
|
|
|
//
|
|
int dotLocation = exceptionName.LastIndexOf(".");
|
|
if (dotLocation > 0 && dotLocation != exceptionName.Length) {
|
|
type = exceptionName.Substring(dotLocation + 1);
|
|
} else {
|
|
type = exceptionName;
|
|
}
|
|
|
|
int stackLocation = exceptionDetail.IndexOf(" at ");
|
|
if (stackLocation > 0) {
|
|
//
|
|
reason = exceptionDetail.Substring(0, stackLocation);
|
|
// substring after " at "
|
|
string callStacks = exceptionDetail.Substring(stackLocation + 3).Replace(" at ", "\n").Replace("in <filename unknown>:0","").Replace("[0x00000]","");
|
|
//
|
|
stackTrace = string.Format("{0}\n{1}", stackTrace, callStacks.Trim());
|
|
|
|
} else {
|
|
reason = exceptionDetail;
|
|
}
|
|
|
|
// for LuaScriptException
|
|
if(type.Equals("LuaScriptException") && exceptionDetail.Contains(".lua") && exceptionDetail.Contains("stack traceback:")) {
|
|
stackLocation = exceptionDetail.IndexOf("stack traceback:");
|
|
if(stackLocation > 0) {
|
|
reason = exceptionDetail.Substring(0, stackLocation);
|
|
// substring after "stack traceback:"
|
|
string callStacks = exceptionDetail.Substring(stackLocation + 16).Replace(" [", " \n[");
|
|
|
|
//
|
|
stackTrace = string.Format("{0}\n{1}", stackTrace, callStacks.Trim());
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
} catch {
|
|
|
|
}
|
|
|
|
if (string.IsNullOrEmpty (reason)) {
|
|
reason = message;
|
|
}
|
|
}
|
|
|
|
if (string.IsNullOrEmpty (name)) {
|
|
if (string.IsNullOrEmpty (type)) {
|
|
type = string.Format ("Unity{0}", logLevel.ToString ());
|
|
}
|
|
} else {
|
|
type = name;
|
|
}
|
|
|
|
_reportException (uncaught, type, reason, stackTrace);
|
|
}
|
|
|
|
private static bool _uncaughtAutoReportOnce = false;
|
|
|
|
#endregion
|
|
|
|
} |