手机中可以通过 Application.logMessageReceivedThreaded来监听日志的信息,在通过File.AppendAllText来将日志写在手机的Application.persistentDataPath目录中。日志可以按天写入,这样即使游戏闪退也可以把当天产生的日志取出来。
测试机取日志很麻烦,出现问题的时候周围也不一定有电脑可以取,而且测试机上也不一定安装飞书企业微信这种软件,就算安装了使用者也未必会取,未必会发出来。所以我就想是否能利用飞书开发平台的API自动将这个文件发出来。
1.需要在飞书开放平台上创建一个企业自建机器人应用,并且开通上传文件和发送消息权限(需要企业管理员审核)
2.通过app id和app secret 获取token,上传文件,并且发送到指定的群聊中(需要提前将企业自建机器人加入群聊)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
string logPath = $"{Application.persistentDataPath}/1.log"; File.WriteAllText(logPath, "unity中产生的日志"); Feishu.Send("appid", "appsecret", "群聊chatid", logPath, () => { Debug.Log("发送成功"); }, (error) => { Debug.Log("发送失败"); }); |
群聊的chatid在飞书开发平台中获取。由于日志文件可能比较大,我还做了一次压缩,接口我已经封装好了,使用起来很方便。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 |
using System; using System.Collections; using System.IO; using System.IO.Compression; using System.Text; using UnityEngine; using UnityEngine.Networking; public class Feishu:MonoBehaviour { static Feishu Inst; static Feishu() { Inst = new GameObject().AddComponent<Feishu>(); GameObject.DontDestroyOnLoad(Inst.gameObject); } private const string REQUEST_TOKEN_URL = "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal"; private const string REQUEST_UPLOADFILE_URL = "https://open.feishu.cn/open-apis/im/v1/files"; private const string REQUEST_SENDCHAT_URL = "https://open.feishu.cn/open-apis/im/v1/messages?receive_id_type=chat_id"; /// <summary> /// 飞书聊天群发文件 /// </summary> /// <param name="app_id">飞书平台id</param> /// <param name="app_secret">飞书平台secret</param> /// <param name="groupid">聊天窗口id</param> /// <param name="path">文件路径</param> /// <param name="success">成功</param> /// <param name="error">失败</param> public static void Send(string app_id, string app_secret, string groupid, string path, Action success, Action<string> error) { Inst.StartCoroutine(UploadFile(app_id, app_secret, groupid, path, success, error)); } static IEnumerator UploadFile(string app_id, string app_secret,string groupid,string path,Action success, Action<string>error) { string tenant_access_token=null; string file_key = null; string json = $"{{\"app_id\":\"{app_id}\",\"app_secret\":\"{app_secret}\"}}"; bool isFinish = false; using (UnityWebRequest www = UnityWebRequest.Post(REQUEST_TOKEN_URL, json, "")) { www.SetRequestHeader("Content-Type", "application/json; charset=utf-8"); yield return www.SendWebRequest(); if (www.result != UnityWebRequest.Result.Success) { error?.Invoke(www.error); } else { var token = JsonUtility.FromJson<Token>(www.downloadHandler.text); if (token.code == 0) { tenant_access_token = token.tenant_access_token; } else { error?.Invoke(www.downloadHandler.text); } } } if (!string.IsNullOrEmpty(tenant_access_token)) { WWWForm from = new WWWForm(); from.AddField("file_type", "stream"); from.AddField("file_name", Path.GetFileNameWithoutExtension(path)+".zip"); using (var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) using (var streamReader = new StreamReader(fileStream)) { var txt = streamReader.ReadToEnd(); using (var outStream = new MemoryStream()) { using (var archive = new ZipArchive(outStream, ZipArchiveMode.Create, true)) { var fileInArchive = archive.CreateEntry(Path.GetFileName(path)); using (var entryStream = fileInArchive.Open()) using (var fileToCompressStream = new MemoryStream(Encoding.UTF8.GetBytes(txt))) { fileToCompressStream.CopyTo(entryStream); } } from.AddBinaryData("file",outStream.ToArray()); } } using (UnityWebRequest www = UnityWebRequest.Post(REQUEST_UPLOADFILE_URL, from)) { www.SetRequestHeader("Authorization", $"Bearer {tenant_access_token}"); yield return www.SendWebRequest(); if (www.result != UnityWebRequest.Result.Success) { error?.Invoke(www.error); } else { var file = JsonUtility.FromJson<FileKey>(www.downloadHandler.text); if (file.code == 0) { Debug.Log(www.downloadHandler.text); file_key = file.data.file_key; } else { error?.Invoke(www.downloadHandler.text); } } } } if (!string.IsNullOrEmpty(file_key)) { json = $"{{\"receive_id\": \"{groupid}\",\"content\": \"{{\\\"file_key\\\":\\\"{file_key}\\\"}}\",\"msg_type\": \"file\"}}"; using (UnityWebRequest www = UnityWebRequest.Post(REQUEST_SENDCHAT_URL, json,"")) { www.SetRequestHeader("Authorization", $"Bearer {tenant_access_token}"); www.SetRequestHeader("Content-Type", "application/json; charset=utf-8"); yield return www.SendWebRequest(); if (www.result != UnityWebRequest.Result.Success) { error?.Invoke(www.error); } else { var post = JsonUtility.FromJson<Post>(www.downloadHandler.text); if(post.code == 0) { isFinish = true; }else error?.Invoke(www.downloadHandler.text); } } } if (isFinish) { success?.Invoke(); } else { error?.Invoke("未知"); } } [System.Serializable] class Token { public int code; public string tenant_access_token; } [System.Serializable] class Data { public string file_key; } [System.Serializable] class FileKey { public int code; public Data data; } [System.Serializable] class Post { public int code; } } |
从此以后我们和测试、策划之间的沟通就变成了, “把日志发群里” 无论它手机是否出现过报错、闪退,当天的日志都会写在一个文件中发出来,有了这个小工具,极大的提高了我们的开发调Bug的效率。
- 本文固定链接: https://www.xuanyusong.com/archives/5086
- 转载请注明: 雨松MOMO 于 雨松MOMO程序研究院 发表
捐 赠写博客不易,如果您想请我喝一杯星巴克的话?就进来看吧!
说的不错
感谢雨松,我也把流程跑通了,主要有几个问题处理了下
1.需要注释掉以下代码,这会导致协议错误400
http://www.SetRequestHeader(“Content-Type”, “application/json; charset=utf-8”);
2.直接Post会提示属性错误,查了一下可能是URLEncode的问题,对应博主的文章在下面
https://blog.csdn.net/qq_38721111/article/details/130242496
可能是unity版本问题吗? 我用的是unitiy2022.3.20f 目前这段代码在PC和手机上都没有问题。我也在关注下你说的问题。
我的是2021.3.19,有可能新版本对这些情况进行了处理,毕竟我查下来发现还是不少这个问题的讨论的。而且post的接口也不太一样,你的代码里面多了第三个空字符串的参数,2021里只有两个参数