当前位置: 首页 > news >正文

MAUI Blazor学习21-使用NLog记录安卓APP运行日志

MAUI Blazor学习21-使用NLog记录安卓APP运行日志

MAUI Blazor学习21-使用NLog记录安卓APP运行日志 

MAUI Blazor系列目录

  1. MAUI Blazor学习1-移动客户端Shell布局 - SunnyTrudeau - 博客园 (cnblogs.com)
  2. MAUI Blazor学习2-创建移动客户端Razor页面 - SunnyTrudeau - 博客园 (cnblogs.com)
  3. MAUI Blazor学习3-绘制ECharts图表 - SunnyTrudeau - 博客园 (cnblogs.com)
  4. MAUI Blazor学习4-绘制BootstrapBlazor.Chart图表 - SunnyTrudeau - 博客园 (cnblogs.com)
  5. MAUI Blazor学习5-BLE低功耗蓝牙 - SunnyTrudeau - 博客园 (cnblogs.com)
  6. MAUI Blazor学习6-扫描二维码 - SunnyTrudeau - 博客园 (cnblogs.com)
  7. MAUI Blazor学习7-实现登录跳转页面 - SunnyTrudeau - 博客园 (cnblogs.com)
  8. MAUI Blazor学习8-支持多语言 - SunnyTrudeau - 博客园 (cnblogs.com)
  9. MAUI Blazor学习9-VS Code开发调试MAUI入门 - SunnyTrudeau - 博客园 (cnblogs.com)
  10. MAUI Blazor学习10-BarcodeScanner扫描二维码 - SunnyTrudeau - 博客园 (cnblogs.com)
  11. MAUI Blazor学习11-百度地图定位 - SunnyTrudeau - 博客园 (cnblogs.com)
  12. MAUI Blazor学习12-文件另存为 - SunnyTrudeau - 博客园 (cnblogs.com)
  13. MAUI Blazor学习13-打开文件 - SunnyTrudeau - 博客园 (cnblogs.com)
  14. MAUI Blazor学习14-选择目录 - SunnyTrudeau - 博客园 (cnblogs.com)
  15. MAUI Blazor学习15-采用html2pdf.js生成pdf - SunnyTrudeau - 博客园 (cnblogs.com)
  16. MAUI Blazor学习16-连续按BACK退出APP - SunnyTrudeau - 博客园 (cnblogs.com)
  17. MAUI Blazor学习17-NavigationLock阻止页面回退 - SunnyTrudeau - 博客园 (cnblogs.com)
  18. MAUI Blazor学习18-自动升级 - SunnyTrudeau - 博客园
  19. MAUI Blazor学习19-角标(右上角红点) - SunnyTrudeau - 博客园
  20. MAUI Blazor学习20-升级到Net8 - SunnyTrudeau - 博客园

 

对于.Net Core框架的应用软件而言,记录日志是非常基本的功能,也有很多成熟的第三方日志组件。但是在MAUI安卓APP中记录日志还是有一些值得注意的地方,总结一下。

 

引用NLog日志组件

Web项目可以引用NLog.Web.AspNetCore,但是MAUI项目不可以,否则编译报错:Microsoft.AspNetCore.App 没有运行时包可用于指定的 RuntimeIdentifierandroid-arm64”。MAUI项目引用的组件为NLog.Extensions.Logging

<PackageReference Include="NLog.Extensions.Logging" Version="5.3.14" />

 

编写NLog.config

根据项目需要编写NLog.config,需要注意手机APP运行的环境跟Web项目差别很大,建议只记录必要的日志,以免影响性能。

 

<?xml version="1.0" ?><nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"autoReload="true"><targets><!--write logs to Console--><target name="console" xsi:type="ColoredConsole"layout="${longdate}, ${level:uppercase=true:padding=-5}, ${callsite}, ${message}, ${exception}" /><!--write logs to Visual Studio Output--><target name="debugger" xsi:type="Debugger"layout="${longdate}, ${level:uppercase=true:padding=-5}, ${callsite}, ${message}, ${exception}" /><!--write logs to file--><target name="file"  xsi:type="File"layout="${longdate}, ${level:uppercase=true:padding=-5}, ${callsite}, ${message}, ${exception}"fileName="${basedir}/app.log"archiveFileName="${basedir}/app.{#}.log"encoding="utf-8"archiveAboveSize="102400"maxArchiveFiles="10"archiveNumbering="Rolling"concurrentWrites="true"keepFileOpen="false" /></targets><rules><!--TRACE,DEBUG,INFO,WARN,ERROR,FATAL--><logger name="Microsoft.*" maxlevel="Info" writeTo="" final="true" /><logger name="*" minlevel="Debug" writeTo="console" /><logger name="*" minlevel="Debug" writeTo="debugger" /><logger name="*" minlevel="Info" writeTo="file" /></rules>
</nlog>

 

 

注册NLog日志服务

NLog组件有注册服务的接口,使用非常简单。如果是Web项目,可以把NLog.config文件属性设置为总是复制到输出目录,软件启动时就可以加载配置文件。但是MAUI项目行不通,必须编写额外的代码,把NLog.config文件从资源文件目录,复制到APP数据目录。为此编写一个文件帮助类,可以从资源目录加载文件,读写APP数据目录的文件。

D:\Software\gitee\mauiblazorapp\MaBlaApp\Data\FileHelper.cs

/// <summary>
/// 文件帮助类
/// </summary>
public static class FileHelper
{/// <summary>/// 加载Raw目录下的文本文件/// </summary>/// <param name="filename"></param>/// <returns></returns>public static async Task<string> LoadTxtFromMauiAssetAsync(string filename){using var stream = await FileSystem.OpenAppPackageFileAsync(filename);using var reader = new StreamReader(stream);var contents = reader.ReadToEnd();Debug.WriteLine($"加载{filename}, 长度={contents.Length:N0}");return contents;}/// <summary>/// 保存文本文件到APP数据目录/// </summary>/// <param name="filename"></param>/// <param name="contents"></param>/// <returns></returns>public static async Task<bool> SaveTxtToAppDataAsync(string filename, string contents){string filePath = Path.Combine(FileSystem.Current.AppDataDirectory, filename);try{if (File.Exists(filePath)){// 如果文件内容没有变化,则不需要保存string oldContents = await File.ReadAllTextAsync(filePath);if (oldContents == contents){Debug.WriteLine($"文件{filePath}内容没有变化,不需要保存");return true;}}await File.WriteAllTextAsync(filePath, contents);Debug.WriteLine($"保存{filePath}, 长度={contents.Length:N0}");return true;}catch (Exception ex){Debug.WriteLine(ex);return false;}}/// <summary>/// 加载APP数据目录下的文本文件/// </summary>/// <param name="filename"></param>/// <returns></returns>public static async Task<string> LoadTxtFromAppDataAsync(string filename){string filePath = Path.Combine(FileSystem.Current.AppDataDirectory, filename);try{if (!File.Exists(filePath))return "";string contents = await File.ReadAllTextAsync(filePath);Debug.WriteLine($"加载{filePath}, 长度={contents.Length:N0}");return contents;}catch (Exception ex){Debug.WriteLine(ex);return "";}}
}

编写APP日志帮助类,封装注册NLog服务。安装APP后首次运行没有NLog.config文件,无法注册NLog日志服务;第二次以后运行就可以了。

D:\Software\gitee\mauiblazorapp\MaBlaApp\Data\AppLogHelper.cs

/// <summary>
/// APP日志帮助类
/// </summary>
public static class AppLogHelper
{const string NLogConfigFileName = "NLog.config";/// <summary>/// 注册NLog日志服务/// </summary>/// <param name="services"></param>public static void AddLogServices(this IServiceCollection services){//复制NLog配置文件到AppData目录InitNLogConfigAsync().ConfigureAwait(false);services.AddLogging(builder =>{// 移除已经注册的其他日志处理程序
            builder.ClearProviders();builder.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Debug);string nlogConfigFile = Path.Combine(AppContext.BaseDirectory, NLogConfigFileName);if (File.Exists(nlogConfigFile)){LogManager.Configuration = new XmlLoggingConfiguration(nlogConfigFile);builder.AddNLog(LogManager.Configuration);Debug.WriteLine($"{nlogConfigFile}文件存在,NLog日志服务注册成功");}else{Debug.WriteLine($"{nlogConfigFile}文件不存在,NLog日志服务未注册");}});}/// <summary>/// 复制NLog配置文件到AppData目录/// </summary>/// <returns></returns>public static async Task<bool> InitNLogConfigAsync(){//加载Raw目录下的NLog配置文件string contents = await FileHelper.LoadTxtFromMauiAssetAsync(NLogConfigFileName);//保存NLog配置文件到APP数据目录bool success = await FileHelper.SaveTxtToAppDataAsync(NLogConfigFileName, contents);return success;}
}

 

编写测试日志的服务和页面模块

编写一个测试日志的服务类,生成各种级别的日志消息,把日志文件复制到安卓手机的download目录。

D:\Software\gitee\mauiblazorapp\MaBlaApp\Data\TestLogService.cs

/// <summary>
/// 测试日志服务
/// </summary>
public class TestLogService
{const string AppLogFileName = "app.log";private readonly ILogger<TestLogService> _logger;public TestLogService(ILogger<TestLogService> logger){_logger = logger;}/// <summary>/// 创建日志消息/// </summary>public void MakeLogMessage(){_logger.LogTrace("测试Trace日志");_logger.LogDebug("测试Debug日志");_logger.LogInformation("测试Info日志");_logger.LogWarning("测试Warn日志");_logger.LogError("测试Error日志");_logger.LogCritical("测试Critical日志");}/// <summary>/// 导出APP日志文件/// </summary>/// <returns></returns>public async Task<int> ExportAppLogAsync(){//加载APP日志文件内容//[0:] 加载/data/user/0/com.companyname.mablaapp/files/app.log, 长度=xxxstring contents = await FileHelper.LoadTxtFromAppDataAsync(AppLogFileName);// 获取download目录
#if ANDROIDvar downloadFile = Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryDownloads);string downloadPath = downloadFile.AbsolutePath;
#elsestring downloadPath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
#endifstring downloadFilePath = Path.Combine(downloadPath, AppLogFileName);await File.WriteAllTextAsync(downloadFilePath, contents);//[0:] 导出APP日志文件到/storage/emulated/0/Download/app.log, 长度=xxxDebug.WriteLine($"导出APP日志文件到{downloadFilePath}, 长度={contents.Length:N0}");return contents.Length;}
}

注册日志服务

D:\Software\gitee\mauiblazorapp\MaBlaApp\MauiProgram.cs

    public static MauiApp CreateMauiApp()

 

        //注册NLog日志服务

        AppLogHelper.AddLogServices(builder.Services);

 

        //注册日志服务

        builder.Services.AddScoped<TestLogService>();

 

创建测试导出日志的页面

D:\Software\gitee\mauiblazorapp\MaBlaApp\Pages\ExportAppLog.razor

@page "/exportapplog"
@using System.Diagnostics@inject TestLogService LogService<h3>导出APP日志文件</h3>
<button class="btn btn-primary mx-2" @onclick=ExportAppLogFileAsync>导出</button>
<p class="m-4" role="status">@Msg</p>@code {private string Msg = "";protected override async Task OnInitializedAsync(){//创建日志消息
        LogService.MakeLogMessage();}private async Task ExportAppLogFileAsync(){try{//导出APP日志文件int fileLen = await LogService.ExportAppLogAsync();Msg = $"{DateTimeOffset.Now}, 导出日志文件大小{fileLen:N0}";}catch (Exception ex){// 处理异常Debug.WriteLine($"导出日志文件失败: {ex.Message}");}}
}

可以运行项目,测试导出APP日志。然后在手机的download目录查看导出的日志文件。

210

首次运行没有日志服务

[0:] /data/user/0/com.companyname.mablaapp/files/NLog.config文件不存在,NLog日志服务未注册

 

第二次以后运行有日志服务了

[0:] /data/user/0/com.companyname.mablaapp/files/NLog.config文件存在,NLog日志服务注册成功

 

查看导出的日志文件,跟NLog.config配置是符合的。

2025-08-24 15:18:35.3640, INFO , MaBlaApp.Data.TestLogService.MakeLogMessage, 测试Info日志,

2025-08-24 15:18:35.3838, WARN , MaBlaApp.Data.TestLogService.MakeLogMessage, 测试Warn日志,

2025-08-24 15:18:35.3908, ERROR, MaBlaApp.Data.TestLogService.MakeLogMessage, 测试Error日志,

2025-08-24 15:18:35.3957, FATAL, MaBlaApp.Data.TestLogService.MakeLogMessage, 测试Critical日志,

 

遗留问题

安装APP后首次运行没有NLog.config文件,无法注册NLog日志服务;第二次以后运行就可以了。因为从资源目录复制文件到APP数据目录,采用了异步函数。如果想要解决这个问题,也是可以的,比如采用读写文件的同步函数。不建议调用异步函数的GetAwaiter().GetResult(),有死锁风险。

 

DEMO代码地址:https://gitee.com/woodsun/mauiblazorapp

 

http://www.sczhlp.com/news/34159/

相关文章:

  • VLLM进行LLM推理
  • 具有品牌的广州做网站情感营销的十大案例
  • 爱建站小程序特点河南搜索引擎优化
  • 百度小程序优化合作公司百度seo关键词优化公司
  • 苏州室内设计公司宁波seo关键词培训
  • 成都市网站建设百度发布平台官网
  • 大模型刷榜单
  • 给mysql root用户远程访问权限
  • 政务网站开发协议站长工具seo综合查询源码
  • 哪些网站做的比较好看的图片推广方案策划
  • 纯文字网站设计拉新平台哪个好佣金高
  • 做网站每天更新两篇文章深圳关键词快速排名
  • 中国网站建设第一品牌广告投放网站平台
  • 少儿编程加盟费用湖南专业seo公司
  • 职教集团网站建设方案网上营销方式和方法
  • 网站可以先做后再申请域名吗青岛网站排名推广
  • 个人工商户做网站备案网络优化seo薪酬
  • 计算机专业主要学什么好就业优化落实疫情防控
  • 合肥网站建设新浪营销免费域名注册申请
  • 网站后台和移动开发网络推广是什么专业
  • 哪里去找做的好看的网站西安外包网络推广
  • 可以做t恤的网站成都网站搜索排名优化公司
  • 网站怎么更新内容惠州seo关键词排名
  • 变现流量推广app河北seo推广方案
  • 在手机怎样使用wordpress优化营商环境条例心得体会
  • 网站怎么做301定向长沙网络推广只选智投未来
  • 北京国互网网站建设公司网络营销总监岗位职责
  • Linux文件压缩与解压
  • 红绿灯的 “时间” 谁来定​
  • Linux环境变量详解