aspnetcore进程内托管的坑-非常规方法解决Log4Net不写日志的问题

问题描述:Log4Net,本地测试一切正常,发布后,无法自动创建文件夹和日志文件,无法写入文件。

一、在项目中配置Log4Net

请参考我的上一篇博客 《aspnetcore配置log4net并添加全局异常处理》,常规做法。

二、Log4Net不写日志常规解决步骤

一般讲来,Log4Net是非常成熟的框架,很难出现问题,出现不写日志这种情况,首先要做的是检查我们的代码、配置是否正确。

  1. 检查目录中是否包含 log4net.config ,如果文件不存在,手动复制一份即可。
  2. 检查  log4net.config 文件,主要是日志文件输出路径,一般日志保存目录是这样的  根目录/Logs/Errors/xxxx.log ,其他资料里一般写法为  <file value="Logs\\Errors\\" /> ,在这里个人推荐这样来写  <file value="Logs/Errors/" /> ,原因是曾经在  ubuntu > nginx  环境下部署时,第一种写法无法正常工作,改为第二种方法后正常。
  3. 检查文件夹权限,确保具有写权限。

三、绝望的排查过程

因为在本地测试完全正常,所以代码和配置文件应该没有问题,但保险起见,还是将网上常用的几种写法都尝试了一遍,然而,并没有什么卵用。

接下来,查看文件夹权限,讲真,对于Server的文件系统我是真的了解很少,大部分时间遇到权限问题,就简单的给Everyone一个读写权限了事。不出意外的,折腾许久,还是失败了。

重复以上步骤两个小时后...

 头昏脑涨中,想起之前的项目中使用Log4Net是正常的,写法和部署方式一样,为什么这次不行呢?

于是对比之前的项目,把IIS中站点和应用程序池的配置项一条一条拿出来看,一个小时后...

完全一样啊,啥情况,要不换NLog???虽然为了项目进度换成其他框架也说的过去,但作为程序员,明知道有bug却解决不了,总归过不去心里这一关不是。不甘心啊~

四、灵光乍现

就在准备把问题先放放,修改项目使用Nlog的时候,鬼使神差的,去看了眼任务管理器,咦~~还真发现了问题,之前的项目都是开了三个进程(w3wp.exe \ conhost.exe \ dotnet.exe),而新项目只有两个(w3wp.exe \ conhost.exe),精神立马振奋了一下,看来问题可能就在这里了。打开项目的  web.config  文件,发现多了点东西:

有问题找度娘,搜索 InProcess ,大部分内容都一样,没看出啥问题,难道是我想错了,问题不在这里?说脏话会不会发布失败?咱也不知道咱也不敢问,还是憋回去吧!

抱着最后一丝希望,跑到官方文档 《ASP.NET Core 模块》,不得不说啊,微软自动翻译的文档真的很考验中文水平,但是再烂也得看不是,这块内容之前也没说关注过,不管能不能解决问题,看看也好,当补课了。

细细看下来,有这么一段

在 ASP.NET Core 2.2.1 或早期版本中,GetCurrentDirectory 会返回 IIS 启动的进程的工作目录而非应用目录(例如,对于 w3wp.exe,是 C:\Windows\System32\inetsrv)。

对于设置应用的当前目录的示例代码,请参阅 CurrentDirectoryHelpers 类。 调用 SetCurrentDirectory 方法。 后续 GetCurrentDirectory 调用提供应用的目录。

醍醐灌顶,虽然外面已经是傍晚时分,但我眼前仿佛一道曙光升起,恍惚间似乎看到天女降临,嘿,嘿嘿~~

五、填坑

 接下来就简单了,github上把CurrentDirectoryHelpers类搞下来,在  startup.cs  的构造函数中添加  CurrentDirectoryHelpers.SetCurrentDirectory(); ,重新发布、部署、启动站点,文件出现了。

下面贴一下  CurrentDirectoryHelpers  的实现,不要谢我,我只是代码的搬运工

using System;

namespace SampleApp
{
    internal class CurrentDirectoryHelpers
    {
        internal const string AspNetCoreModuleDll = "aspnetcorev2_inprocess.dll";

        [System.Runtime.InteropServices.DllImport("kernel32.dll")]
        private static extern IntPtr GetModuleHandle(string lpModuleName);

        [System.Runtime.InteropServices.DllImport(AspNetCoreModuleDll)]
        private static extern int http_get_application_properties(ref IISConfigurationData iiConfigData);

        [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
        private struct IISConfigurationData
        {
            public IntPtr pNativeApplication;
            [System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.BStr)]
            public string pwzFullApplicationPath;
            [System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.BStr)]
            public string pwzVirtualApplicationPath;
            public bool fWindowsAuthEnabled;
            public bool fBasicAuthEnabled;
            public bool fAnonymousAuthEnable;
        }

        public static void SetCurrentDirectory()
        {
            try
            {
                // Check if physical path was provided by ANCM
                var sitePhysicalPath = Environment.GetEnvironmentVariable("ASPNETCORE_IIS_PHYSICAL_PATH");
                if (string.IsNullOrEmpty(sitePhysicalPath))
                {
                    // Skip if not running ANCM InProcess
                    if (GetModuleHandle(AspNetCoreModuleDll) == IntPtr.Zero)
                    {
                        return;
                    }

                    IISConfigurationData configurationData = default(IISConfigurationData);
                    if (http_get_application_properties(ref configurationData) != 0)
                    {
                        return;
                    }

                    sitePhysicalPath = configurationData.pwzFullApplicationPath;
                }

                Environment.CurrentDirectory = sitePhysicalPath;
            }
            catch
            {
                // ignore
            }
        }
    }
}
View Code

 

posted @ 2019-06-02 18:12  没追求的码农  阅读(1785)  评论(1编辑  收藏  举报