Unity输出Log开发日记到本地文件,Unity Log 本地化管理

本文介绍两种在Unity中实现日志记录的方法,包括初始化日志记录器、写入信息到本地文件的过程,并提供了详细的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

环境:unity5.3,C#

目的:将信息写入到本地文件,方便查看。

方法一:

/*******************************************************************
Copyright © framework
Description: 日志输出
android日志放在Android/data/com.xxx.yyy/files/目录下
ios无法生成日记
Date: 2021-2-4
********************************************************************/

using System.IO;
using UnityEngine;
using System.Text;

public class Logger
{
    public void InitLogger()
    {
        string fileName = "outputlog.txt";
#if UNITY_EDITOR
        System.IO.DirectoryInfo dir = new DirectoryInfo(Application.dataPath);
        var fullSavePath = string.Format("{0}/{1}", dir.Parent.FullName, fileName);
        InitLogger(fullSavePath);
#elif UNITY_ANDROID
        //var fullSavePath = "mnt/sdcard/outputlog.txt";
        var fullSavePath = string.Format("{0}/Log/{1}", Application.persistentDataPath, fileName);
        InitLogger(fullSavePath);
#elif UNITY_IPHONE
        var fullSavePath = string.Format("{0}/{1}", Application.persistentDataPath, fileName);
        InitLogger(fullSavePath);
#elif UNITY_STANDALONE_WIN
        var fullSavePath = string.Format("{0}/{1}", Application.dataPath, fileName);
        InitLogger(fullSavePath);
#endif
    }

    void InitLogger(string fullSaveFile)
    {
        m_fullSaveFile = fullSaveFile;

        FileInfo fileInfo = new FileInfo(m_fullSaveFile);
        string path = fileInfo.DirectoryName;
        Debug.LogError(string.Format("---- path = {0}", path));

        if (!Directory.Exists(path))
        {
            Directory.CreateDirectory(path);
        }

        try
        {
            // 创建一个新文件,向其中写入指定的字符串,然后关闭文件。 如果目标文件已存在,则覆盖该文件。
            System.IO.File.WriteAllText(m_fullSaveFile, string.Empty, Encoding.UTF8);
            Debug.Log("Logger Start : " + path);
        }
        catch (System.Exception ex)
        {
            Debug.Log("error can not open." + ex.StackTrace.ToString() + ex.Message);
        }
    }

    private void logCallBack(string condition, string stackTrace, LogType type)
    {
        lock (s_locker)
        {
            if ((type == LogType.Error || type == LogType.Exception))
            {
                m_needUpload = true;
            }

            string strTime = System.DateTime.Now.ToString("MM-dd HH:mm:ss");
            string log = string.Format("{0} curframe:{1} {2}\n", strTime, Time.frameCount, condition);
            m_logBuilder.Append(log);
            m_logBuilder.Append(stackTrace);
            m_logBuilder.Append("\n");
            //m_logBuilder.Append("-xxx\n");
            //m_logBuilder.Append(GetStackTrace());
            Flush();
        }
    }

    private void Flush()
    {
        if (m_logBuilder.Length > 0 && enable)
        {
            //创建一个 StreamWriter,它将 UTF-8 编码文本追加到现有文件或新文件(如果指定文件不存在)。
            //https://docs.qq.com/sheet/DRGRsTXRVRmFWQkJM
            using (StreamWriter sw = File.AppendText(m_fullSaveFile))
            {
                sw.WriteLine(m_logBuilder.ToString());
            }
            //m_logBuilder.Remove(0, m_logBuilder.Length);
            // 从当前 StringBuilder 实例中移除所有字符。
            m_logBuilder.Clear();
        }
    }

    /// <summary>
    ///复制一份log,用来查找bug
    /// </summary>
    public void CopyOutPutLog()
    {
        try
        {
            if (File.Exists(m_fullSaveFile))
            {
                string file = File.ReadAllText(m_fullSaveFile);
                if (file.Length > 0)
                {
                    string copyFile = m_fullSaveFile.Replace("/outputlog.txt", "/outputlog_1.txt");
                    if (File.Exists(copyFile))
                    {
                        FileInfo copyFileInfo = new FileInfo(copyFile);
                        if (copyFileInfo.Length > MaxFileSize_1)
                        {
                            //创建一个新文件,使用指定编码向其中写入指定的字符串,然后关闭文件。 如果目标文件已存在,则覆盖该文件。
                            //https://docs.qq.com/sheet/DRGRsTXRVRmFWQkJM
                            File.WriteAllText(copyFile, string.Empty, Encoding.UTF8);
                        }
                    }

                    //打开一个文件,向其中追加指定的字符串,然后关闭该文件。 如果文件不存在,此方法将创建一个文件,将指定的字符串写入文件,然后关闭该文件。
                    //https://docs.qq.com/sheet/DRGRsTXRVRmFWQkJM
                    File.AppendAllText(copyFile, file);
                }
            }
        }
        catch (System.Exception e)
        {
            Debug.LogError(e);
        }
    }

    public static Logger instance
    {
        get
        {
            if (s_instance == null)
            {
                lock (s_locker)
                {
                    if (s_instance == null)
                    {
                        s_instance = new Logger();
                    }
                }
            }

            return s_instance;
        }
    }

    private Logger()
    {
        m_logBuilder = new StringBuilder();
        m_logBuilder.Clear();
        enable = true;

        // 这里注意:注册Unity日记回调
        //Application.logMessageReceivedThreaded += logCallBack;
        Application.logMessageReceived += logCallBack;
    }

    public bool enable;
    private bool m_needUpload;
    private string m_fullSaveFile = string.Empty;
    private StringBuilder m_logBuilder;
    private static System.Object s_locker = new System.Object();
    private static Logger s_instance;

    private const long MaxFileSize = 0;
    private const long MaxFileSize_1 = 5 * 1024 * 1024;

    /// <summary>
    /// 获取调用堆栈信息
    /// </summary>
    private string GetStackTrace()
    {
        System.Diagnostics.StackTrace st = new System.Diagnostics.StackTrace(true);
        string stackIndent = "";
        for (int i = 0; i < st.FrameCount; i++)
        {
            System.Diagnostics.StackFrame sf = st.GetFrame(i);

            //得到错误的方法
            stackIndent += sf.GetMethod() + "";
            //得到错误的文件名
            stackIndent += sf.GetFileName() + "";
            //得到文件错误的行号
            stackIndent += (":" + sf.GetFileLineNumber()) + "";
            //得到错误的列
            //stackIndent += " column:" + sf.GetFileColumnNumber() + " ";

            stackIndent += "\n";
        }
        return stackIndent;
    }
}

方法二:

using UnityEngine;
//using System.Collections;
using System.IO;

/// <summary>
/// 写文件
/// </summary>
public class WriteLog
{
    private string m_infoFile;

    private static readonly object s_lock = new object();
    private static WriteLog s_instance;
    public static WriteLog instance
    {
        get
        {
            if (s_instance == null)
            {
                lock (s_lock)
                {
                    if (s_instance == null)
                    {
                        s_instance = new WriteLog();
                    }
                }
            }
            return s_instance;
        }
    }

    private WriteLog()
    {
        Init();
    }

    private void Init()
    {
#if UNITY_EDITOR
        string dir = Application.dataPath;
        int index = dir.LastIndexOf("/");
        dir = dir.Substring(0, index);
        dir += "/GameLog";
        if (!Directory.Exists(dir))
        {
            Directory.CreateDirectory(dir);
        }
        m_infoFile = dir + "/" + "gamelog.txt";
#else
        string dir = string.Format("{0}/{1}", Application.persistentDataPath, "GameLog");
        if (!Directory.Exists(dir))
        {
            Directory.CreateDirectory(dir);
        }
        m_infoFile = dir + "/" + "gamelog.txt";
#endif
    }

    /// <summary>
    /// 获取调用堆栈信息
    /// </summary>
    private string GetStackTrace()
    {
        System.Diagnostics.StackTrace st = new System.Diagnostics.StackTrace(true);
        string stackIndent = "";
        for (int i = 0; i < st.FrameCount; i++)
        {
            System.Diagnostics.StackFrame sf = st.GetFrame(i);

            //得到错误的方法
            stackIndent += sf.GetMethod() + "";
            //得到错误的文件名
            stackIndent += sf.GetFileName() + "";
            //得到文件错误的行号
            stackIndent += (":" + sf.GetFileLineNumber()) + "";
            //得到错误的列
            //stackIndent += " column:" + sf.GetFileColumnNumber() + " ";

            stackIndent += "\n";
        }
        return stackIndent;
    }

    /// <summary>
    /// 写入信息到本地文件
    /// </summary>
    /// <param name="info">信息</param>
    public void WriteInfo(string info)
    {
        using (StreamWriter sw = File.AppendText(m_infoFile))
        {
            //string stackInfo = new System.Diagnostics.StackTrace().ToString();
            string stackInfo = GetStackTrace();
            info += ("\n" + stackInfo);
            sw.WriteLine(info);
        }
    }
}

 

 

 

 

 

<think>好的,我现在需要回答用户关于Unity WebGL打包后如何实现读写本地文件的问题。首先,我需要回忆一下Unity WebGL的限制。由于浏览器安全策略,WebGL应用无法直接访问本地文件系统,这一点用户可能已经知道,但需要明确说明。 接下来,我需要考虑可能的解决方案。常见的办法是使用浏览器提供的API,比如JavaScript的File API,让用户主动选择文件进行读写。Unity可以通过与JavaScript交互来实现这一点。例如,用户点击按钮触发文件选择,读取文件内容后传给Unity,或者从Unity导出数据到JavaScript,再保存本地。 另外,可能需要使用持久化存储,比如浏览器的LocalStorage或IndexedDB。这些方法适合存储较小的数据,比如用户偏好或游戏进度。对于较大的文件,可能需要分块处理或者使用服务器存储,但用户的问题是关于本地文件,所以重点应放在客户端解决方案。 然后,我需要考虑Unity的PlayerPrefs,但它只适合存储简单数据,不适合大文件。所以应该建议结合JavaScript插件来处理。可能需要编写.jslib插件,或者使用Unity的Application.persistentDataPath,但WebGL环境下这个路径是虚拟的,无法直接访问,必须通过浏览器机制。 还需要提到Unity WebGL的安全限制,比如跨域问题和同源策略,这些可能会影响文件访问。解决方案可能涉及配置服务器CORS头,但本地开发时可能需要禁用某些安全设置,但这不适用于生产环境。 最后,要确保用户知道这些方法都需要用户交互,不能静默读写,这是浏览器的安全要求。可能还需要提供示例代码,比如如何调用JavaScript函数,如何处理回调,将数据传递回Unity。 总结步骤:1. 使用浏览器File API进行文件选择读取;2. 使用JavaScript与Unity交互传输数据;3. 使用LocalStorage或IndexedDB存储较小数据;4. 处理大文件可能需要分块或服务器协助;5. 注意安全限制和用户交互的必要性。</think>在Unity WebGL中实现本地文件读写需要结合浏览器API和JavaScript交互,因为WebGL运行在浏览器沙盒环境中,无法直接访问本地文件系统。以下是具体解决方案: ### 一、使用浏览器文件API实现读写 1. **读取本地文件**:通过HTML5的`<input type="file">`实现用户主动选择文件 ```javascript // 创建隐藏的input元素 function CreateFileInput() { var input = document.createElement('input'); input.type = 'file'; input.onchange = function(e) { var file = e.target.files[0]; var reader = new FileReader(); reader.onload = function() { // 将文件内容传给Unity SendMessage('GameObject', 'OnFileLoaded', reader.result); }; reader.readAsText(file); }; input.click(); } ``` 2. **写入本地文件**:使用Blob对象和动态创建下载链接 ```javascript function DownloadFile(content, filename) { var blob = new Blob([content], {type: 'text/plain'}); var link = document.createElement('a'); link.href = URL.createObjectURL(blob); link.download = filename; link.click(); } ``` ### 二、Unity与JavaScript交互 1. 创建.jslib插件文件: ```javascript mergeInto(LibraryManager.library, { WriteLocalFile: function(content, filename) { DownloadFile(Pointer_stringify(content), Pointer_stringify(filename)); }, ReadLocalFile: function() { CreateFileInput(); } }); ``` 2. C#调用接口: ```csharp public class FileManager : MonoBehaviour { [DllImport("__Internal")] private static extern void ReadLocalFile(); [DllImport("__Internal")] private static extern void WriteLocalFile(string content, string filename); // 被JavaScript调用的方法 public void OnFileLoaded(string content) { Debug.Log("Received file content: " + content); } } ``` ### 三、数据持久化方案 1. **浏览器存储方案**: - `LocalStorage`:适合存储<5MB的简单数据 ```csharp // 存储数据 PlayerPrefs.SetString("saveKey", jsonData); PlayerPrefs.Save(); ``` - `IndexedDB`:适合存储结构化大数据[^1] 2. **注意事项**: - 所有文件操作必须由用户主动触发(如点击事件) - 大文件处理需要分块读取/写入 - 文件路径是虚拟的,无法获取真实物理路径 - 使用`Application.persistentDataPath`会指向浏览器的虚拟文件系统 ### 四、高级方案(需要服务器支持) 1. 通过WebSocket实现云端同步 2. 使用WebAssembly文件系统(需要额外的wasm模块) 3. 集成第三方云存储API(如Firebase Storage)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值