You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MvvmCross自定义UDP日志提供器无法捕获框架自身日志问题排查

解决MvvmCross框架日志无法被自定义UDP LogProvider捕获的问题

你的问题主要出在日志接口实现不匹配和上下文方法未正确处理这两个关键点上,我来一步步帮你修复:

1. 核心问题:DebugTrace类的接口不兼容

你当前在UdpLogProvider中返回的DebugTrace大概率还是基于旧的IMvxTrace接口实现的,但MvvmCross从v5版本开始就切换到了IMvxLogProvider/IMvxLog的新日志体系。框架内部现在只会调用IMvxLog的方法(比如Error()、Warn()),而非旧接口的Trace()方法,这就是框架日志无法被捕获的根本原因。

修复方案:重新实现IMvxLog接口

替换你的DebugTrace,创建一个符合新接口要求的UdpMvxLog类,示例代码如下:

public class UdpMvxLog : IMvxLog
{
    private readonly string _loggerName;
    private readonly UdpClient _udpClient;
    private readonly IPEndPoint _remoteEndpoint;

    public UdpMvxLog(string loggerName)
    {
        _loggerName = loggerName;
        // 根据你的实际配置初始化UDP客户端
        _udpClient = new UdpClient();
        _remoteEndpoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 12345);
    }

    // 按需开启对应级别的日志开关
    public bool IsTraceEnabled => true;
    public bool IsDebugEnabled => true;
    public bool IsInfoEnabled => true;
    public bool IsWarnEnabled => true;
    public bool IsErrorEnabled => true;
    public bool IsFatalEnabled => true;

    // 核心日志处理方法,统一处理所有级别日志
    public void Log(MvxLogLevel logLevel, Func<string> messageFunc, Exception exception = null, params object[] formatParameters)
    {
        if (!IsLogLevelEnabled(logLevel)) return;

        var message = messageFunc?.Invoke() ?? string.Empty;
        if (formatParameters?.Length > 0)
        {
            message = string.Format(message, formatParameters);
        }

        if (exception != null)
        {
            message += $"\nException Details: {exception}";
        }

        var formattedLog = $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] [{logLevel}] [{_loggerName}] {message}";
        SendUdpLog(formattedLog);
    }

    // 以下是IMvxLog接口的默认方法,直接转发到核心Log方法即可
    public void Trace(Func<string> messageFunc) => Log(MvxLogLevel.Trace, messageFunc);
    public void Trace(string message, params object[] args) => Log(MvxLogLevel.Trace, () => message, null, args);
    public void Debug(Func<string> messageFunc) => Log(MvxLogLevel.Debug, messageFunc);
    public void Debug(string message, params object[] args) => Log(MvxLogLevel.Debug, () => message, null, args);
    public void Info(Func<string> messageFunc) => Log(MvxLogLevel.Info, messageFunc);
    public void Info(string message, params object[] args) => Log(MvxLogLevel.Info, () => message, null, args);
    public void Warn(Func<string> messageFunc) => Log(MvxLogLevel.Warn, messageFunc);
    public void Warn(string message, params object[] args) => Log(MvxLogLevel.Warn, () => message, null, args);
    public void Warn(Exception exception, Func<string> messageFunc) => Log(MvxLogLevel.Warn, messageFunc, exception);
    public void Warn(Exception exception, string message, params object[] args) => Log(MvxLogLevel.Warn, () => message, exception, args);
    public void Error(Func<string> messageFunc) => Log(MvxLogLevel.Error, messageFunc);
    public void Error(string message, params object[] args) => Log(MvxLogLevel.Error, () => message, null, args);
    public void Error(Exception exception, Func<string> messageFunc) => Log(MvxLogLevel.Error, messageFunc, exception);
    public void Error(Exception exception, string message, params object[] args) => Log(MvxLogLevel.Error, () => message, exception, args);
    public void Fatal(Func<string> messageFunc) => Log(MvxLogLevel.Fatal, messageFunc);
    public void Fatal(string message, params object[] args) => Log(MvxLogLevel.Fatal, () => message, null, args);
    public void Fatal(Exception exception, Func<string> messageFunc) => Log(MvxLogLevel.Fatal, messageFunc, exception);
    public void Fatal(Exception exception, string message, params object[] args) => Log(MvxLogLevel.Fatal, () => message, exception, args);

    private bool IsLogLevelEnabled(MvxLogLevel logLevel)
    {
        return logLevel switch
        {
            MvxLogLevel.Trace => IsTraceEnabled,
            MvxLogLevel.Debug => IsDebugEnabled,
            MvxLogLevel.Info => IsInfoEnabled,
            MvxLogLevel.Warn => IsWarnEnabled,
            MvxLogLevel.Error => IsErrorEnabled,
            MvxLogLevel.Fatal => IsFatalEnabled,
            _ => false
        };
    }

    private void SendUdpLog(string logMessage)
    {
        try
        {
            var data = Encoding.UTF8.GetBytes(logMessage);
            _udpClient.Send(data, data.Length, _remoteEndpoint);
        }
        catch (Exception ex)
        {
            // 捕获UDP发送异常,避免影响主流程
            Console.WriteLine($"UDP日志发送失败: {ex.Message}");
        }
    }
}

2. 修复UdpLogProvider的上下文方法

你的UdpLogProvider中OpenNestedContext和OpenMappedContext抛出了NotImplementedException,MvvmCross在部分日志场景(比如嵌套日志上下文)会调用这些方法,抛出异常会中断日志流程,导致框架日志无法正常输出。

修改这两个方法,提供空实现即可:

public class UdpLogProvider : IMvxLogProvider 
{ 
    private readonly ConcurrentDictionary<string, IMvxLog> _logs = new ConcurrentDictionary<string, IMvxLog>(); 
    public IMvxLog GetLogFor<T>() 
    { 
        return GetLogFor(typeof(T).Name); 
    } 
    public IMvxLog GetLogFor(string name) 
    { 
        if (_logs.TryGetValue(name, out var log)) 
        { 
            return log; 
        } 
        // 替换为新的UdpMvxLog
        log = new UdpMvxLog(name); 
        _logs.AddOrUpdate(name, log, (s, l) => log); 
        return log; 
    } 
    public IDisposable OpenNestedContext(string message) 
    { 
        // 空实现,也可按需添加上下文处理逻辑
        return new NullDisposable(); 
    } 
    public IDisposable OpenMappedContext(string key, string value) 
    { 
        // 空实现
        return new NullDisposable(); 
    }

    // 辅助类:空的Disposable实现
    private class NullDisposable : IDisposable
    {
        public void Dispose()
        {
            // 无需执行任何操作
        }
    }
}

3. 验证日志级别

绑定错误通常属于Error级别,请确保IsErrorEnabled设置为true,否则框架的错误日志会被过滤掉。

完成以上修改后,重新运行应用,MvvmCross框架内部的日志(比如绑定失败、初始化日志)应该就能被你的UDP LogProvider正常捕获并发送了。

内容的提问来源于stack exchange,提问作者Daniel Z.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 07:20:24