Improve logging architecture; add tests for logging and startup exceptions (#858)

This commit is contained in:
Justin Kotalik 2018-06-01 16:42:58 -07:00 committed by GitHub
parent f09edb3b5d
commit e1ae70166b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 1448 additions and 671 deletions

1
.gitignore vendored
View File

@ -37,6 +37,7 @@ project.lock.json
*.tlog
*.CppClean.log
*msbuild.log
gtest.log
src/*/*/Debug/
src/*/*/x64/Debug/

View File

@ -115,6 +115,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "InProcessRequestHandler", "
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "OutOfProcessRequestHandler", "src\AspNetCoreModuleV2\OutOfProcessRequestHandler\OutOfProcessRequestHandler.vcxproj", "{7F87406C-A3C8-4139-A68D-E4C344294A67}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StartupExceptionWebSite", "test\WebSites\StartupExceptionWebSite\StartupExceptionWebSite.csproj", "{340C59FC-C682-4CBA-81F8-791821EC8EDE}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -359,6 +361,16 @@ Global
{7F87406C-A3C8-4139-A68D-E4C344294A67}.Release|x64.Build.0 = Release|x64
{7F87406C-A3C8-4139-A68D-E4C344294A67}.Release|x86.ActiveCfg = Release|Win32
{7F87406C-A3C8-4139-A68D-E4C344294A67}.Release|x86.Build.0 = Release|Win32
{340C59FC-C682-4CBA-81F8-791821EC8EDE}.Debug|Any CPU.ActiveCfg = Debug|x86
{340C59FC-C682-4CBA-81F8-791821EC8EDE}.Debug|x64.ActiveCfg = Debug|x64
{340C59FC-C682-4CBA-81F8-791821EC8EDE}.Debug|x64.Build.0 = Debug|x64
{340C59FC-C682-4CBA-81F8-791821EC8EDE}.Debug|x86.ActiveCfg = Debug|x86
{340C59FC-C682-4CBA-81F8-791821EC8EDE}.Debug|x86.Build.0 = Debug|x86
{340C59FC-C682-4CBA-81F8-791821EC8EDE}.Release|Any CPU.ActiveCfg = Release|x86
{340C59FC-C682-4CBA-81F8-791821EC8EDE}.Release|x64.ActiveCfg = Release|x64
{340C59FC-C682-4CBA-81F8-791821EC8EDE}.Release|x64.Build.0 = Release|x64
{340C59FC-C682-4CBA-81F8-791821EC8EDE}.Release|x86.ActiveCfg = Release|x86
{340C59FC-C682-4CBA-81F8-791821EC8EDE}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -388,6 +400,7 @@ Global
{48F46909-E76A-4788-BCE1-E543C0E140FE} = {622D35C9-627B-466E-8D15-752968CC79AF}
{D57EA297-6DC2-4BC0-8C91-334863327863} = {06CA2C2B-83B0-4D83-905A-E0C74790009E}
{7F87406C-A3C8-4139-A68D-E4C344294A67} = {06CA2C2B-83B0-4D83-905A-E0C74790009E}
{340C59FC-C682-4CBA-81F8-791821EC8EDE} = {744ACDC6-F6A0-4FF9-9421-F25C5F2DC520}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {DB4F868D-E1AE-4FD7-9333-69FA15B268C5}

View File

@ -1,238 +1,247 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>15.0</VCProjectVersion>
<ProjectGuid>{55494E58-E061-4C4C-A0A8-837008E72F85}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>NewCommon</RootNamespace>
<WindowsTargetPlatformVersion>10.0.15063.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<WholeProgramOptimization>false</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<IncludePath>C:\AspNetCoreModule\src\IISLib;$(IncludePath)</IncludePath>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level4</WarningLevel>
<TreatWarningAsError>true</TreatWarningAsError>
<Optimization>Disabled</Optimization>
<SDLCheck>false</SDLCheck>
<PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<ShowIncludes>false</ShowIncludes>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<AdditionalIncludeDirectories>..\iislib;</AdditionalIncludeDirectories>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level4</WarningLevel>
<TreatWarningAsError>true</TreatWarningAsError>
<Optimization>Disabled</Optimization>
<SDLCheck>false</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<MinimalRebuild>false</MinimalRebuild>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<ShowIncludes>false</ShowIncludes>
<AdditionalIncludeDirectories>..\iislib;</AdditionalIncludeDirectories>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level4</WarningLevel>
<TreatWarningAsError>true</TreatWarningAsError>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>false</SDLCheck>
<PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<ShowIncludes>false</ShowIncludes>
<AdditionalIncludeDirectories>..\iislib;</AdditionalIncludeDirectories>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level4</WarningLevel>
<TreatWarningAsError>true</TreatWarningAsError>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>false</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>..\iislib;</AdditionalIncludeDirectories>
<AdditionalUsingDirectories>
</AdditionalUsingDirectories>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<ShowIncludes>false</ShowIncludes>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
<Lib>
<AdditionalLibraryDirectories>..\iislib</AdditionalLibraryDirectories>
</Lib>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="application.h" />
<ClInclude Include="fx_ver.h" />
<ClInclude Include="hostfxroptions.h" />
<ClInclude Include="hostfxr_utility.h" />
<ClInclude Include="iapplication.h" />
<ClInclude Include="debugutil.h" />
<ClInclude Include="disconnectcontext.h" />
<ClInclude Include="environmentvariablehash.h" />
<ClInclude Include="irequesthandler.h" />
<ClInclude Include="requesthandler.h" />
<ClInclude Include="requesthandler_config.h" />
<ClInclude Include="resources.h" />
<ClInclude Include="SRWLockWrapper.h" />
<ClInclude Include="stdafx.h" />
<ClInclude Include="utility.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="fx_ver.cxx" />
<ClCompile Include="hostfxroptions.cpp" />
<ClCompile Include="hostfxr_utility.cpp" />
<ClCompile Include="requesthandler_config.cpp" />
<ClCompile Include="SRWLockWrapper.cpp" />
<ClCompile Include="stdafx.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="utility.cxx" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\IISLib\IISLib.vcxproj">
<Project>{4787a64f-9a3e-4867-a55a-70cb4b2b2ffe}</Project>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<CustomBuild Include="aspnetcore_msg.mc">
<FileType>Document</FileType>
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">mc %(FullPath)</Command>
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Compiling Event Messages ...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(Filename).rc;%(Filename).h;MSG0409.bin</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">mc %(FullPath)</Command>
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Compiling Event Messages ...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(Filename).rc;%(Filename).h;MSG0409.bin</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">mc %(FullPath)</Command>
<Message Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Compiling Event Messages ...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(Filename).rc;%(Filename).h;MSG0409.bin</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">mc %(FullPath)</Command>
<Message Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Compiling Event Messages ...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(Filename).rc;%(Filename).h;MSG0409.bin</Outputs>
</CustomBuild>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>15.0</VCProjectVersion>
<ProjectGuid>{55494E58-E061-4C4C-A0A8-837008E72F85}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>NewCommon</RootNamespace>
<WindowsTargetPlatformVersion>10.0.15063.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<WholeProgramOptimization>false</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<IncludePath>C:\AspNetCoreModule\src\IISLib;$(IncludePath)</IncludePath>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level4</WarningLevel>
<TreatWarningAsError>true</TreatWarningAsError>
<Optimization>Disabled</Optimization>
<SDLCheck>false</SDLCheck>
<PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<ShowIncludes>false</ShowIncludes>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<AdditionalIncludeDirectories>..\iislib;</AdditionalIncludeDirectories>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level4</WarningLevel>
<TreatWarningAsError>true</TreatWarningAsError>
<Optimization>Disabled</Optimization>
<SDLCheck>false</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<MinimalRebuild>false</MinimalRebuild>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<ShowIncludes>false</ShowIncludes>
<AdditionalIncludeDirectories>..\iislib;</AdditionalIncludeDirectories>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level4</WarningLevel>
<TreatWarningAsError>true</TreatWarningAsError>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>false</SDLCheck>
<PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<ShowIncludes>false</ShowIncludes>
<AdditionalIncludeDirectories>..\iislib;</AdditionalIncludeDirectories>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level4</WarningLevel>
<TreatWarningAsError>true</TreatWarningAsError>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>false</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>..\iislib;</AdditionalIncludeDirectories>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<AdditionalUsingDirectories>
</AdditionalUsingDirectories>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<ShowIncludes>false</ShowIncludes>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
<Lib>
<AdditionalLibraryDirectories>..\iislib</AdditionalLibraryDirectories>
</Lib>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="application.h" />
<ClInclude Include="NullOutputManager.h" />
<ClInclude Include="FileOutputManager.h" />
<ClInclude Include="fx_ver.h" />
<ClInclude Include="hostfxroptions.h" />
<ClInclude Include="hostfxr_utility.h" />
<ClInclude Include="iapplication.h" />
<ClInclude Include="debugutil.h" />
<ClInclude Include="disconnectcontext.h" />
<ClInclude Include="environmentvariablehash.h" />
<ClInclude Include="IOutputManager.h" />
<ClInclude Include="irequesthandler.h" />
<ClInclude Include="LoggingHelpers.h" />
<ClInclude Include="PipeOutputManager.h" />
<ClInclude Include="requesthandler.h" />
<ClInclude Include="requesthandler_config.h" />
<ClInclude Include="resources.h" />
<ClInclude Include="SRWLockWrapper.h" />
<ClInclude Include="stdafx.h" />
<ClInclude Include="sttimer.h" />
<ClInclude Include="utility.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="FileOutputManager.cpp" />
<ClCompile Include="fx_ver.cxx" />
<ClCompile Include="hostfxr_utility.cpp" />
<ClCompile Include="hostfxroptions.cpp" />
<ClCompile Include="requesthandler_config.cpp" />
<ClCompile Include="LoggingHelpers.cpp" />
<ClCompile Include="PipeOutputManager.cpp" />
<ClCompile Include="SRWLockWrapper.cpp" />
<ClCompile Include="stdafx.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="utility.cxx" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\IISLib\IISLib.vcxproj">
<Project>{4787a64f-9a3e-4867-a55a-70cb4b2b2ffe}</Project>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<CustomBuild Include="aspnetcore_msg.mc">
<FileType>Document</FileType>
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">mc %(FullPath)</Command>
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Compiling Event Messages ...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(Filename).rc;%(Filename).h;MSG0409.bin</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">mc %(FullPath)</Command>
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Compiling Event Messages ...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(Filename).rc;%(Filename).h;MSG0409.bin</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">mc %(FullPath)</Command>
<Message Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Compiling Event Messages ...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(Filename).rc;%(Filename).h;MSG0409.bin</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">mc %(FullPath)</Command>
<Message Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Compiling Event Messages ...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(Filename).rc;%(Filename).h;MSG0409.bin</Outputs>
</CustomBuild>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@ -0,0 +1,184 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
#include "stdafx.h"
FileOutputManager::FileOutputManager()
: m_pStdOutFile(NULL)
{
}
FileOutputManager::~FileOutputManager()
{
HANDLE handle = NULL;
WIN32_FIND_DATA fileData;
if (m_pStdOutFile != NULL)
{
m_Timer.CancelTimer();
_close(_fileno(m_pStdOutFile));
m_pStdOutFile = NULL;
}
// delete empty log file
handle = FindFirstFile(m_struLogFilePath.QueryStr(), &fileData);
if (handle != INVALID_HANDLE_VALUE &&
fileData.nFileSizeHigh == 0 &&
fileData.nFileSizeLow == 0) // skip check of nFileSizeHigh
{
FindClose(handle);
// no need to check whether the deletion succeeds
// as nothing can be done
DeleteFile(m_struLogFilePath.QueryStr());
}
_dup2(m_fdStdOut, _fileno(stdout));
_dup2(m_fdStdErr, _fileno(stderr));
}
HRESULT
FileOutputManager::Initialize(PCWSTR pwzStdOutLogFileName, PCWSTR pwzApplicationPath)
{
HRESULT hr = S_OK;
if (SUCCEEDED(hr = m_wsApplicationPath.Copy(pwzApplicationPath)))
{
hr = m_wsStdOutLogFileName.Copy(pwzStdOutLogFileName);
}
return hr;
}
void FileOutputManager::NotifyStartupComplete()
{
}
bool FileOutputManager::GetStdOutContent(STRA* struStdOutput)
{
//
// Ungraceful shutdown, try to log an error message.
// This will be a common place for errors as it means the hostfxr_main returned
// or there was an exception.
//
CHAR pzFileContents[4096] = { 0 };
DWORD dwNumBytesRead;
LARGE_INTEGER li = { 0 };
BOOL fLogged = FALSE;
DWORD dwFilePointer = 0;
if (m_hLogFileHandle != INVALID_HANDLE_VALUE)
{
if (GetFileSizeEx(m_hLogFileHandle, &li) && li.LowPart > 0 && li.HighPart == 0)
{
if (li.LowPart > 4096)
{
dwFilePointer = SetFilePointer(m_hLogFileHandle, -4096, NULL, FILE_END);
}
else
{
dwFilePointer = SetFilePointer(m_hLogFileHandle, 0, NULL, FILE_BEGIN);
}
if (dwFilePointer != INVALID_SET_FILE_POINTER)
{
// Read file fails.
if (ReadFile(m_hLogFileHandle, pzFileContents, 4096, &dwNumBytesRead, NULL))
{
if (SUCCEEDED(struStdOutput->Copy(pzFileContents, dwNumBytesRead)))
{
fLogged = TRUE;
}
}
}
}
}
return fLogged;
}
HRESULT
FileOutputManager::Start()
{
HRESULT hr;
SYSTEMTIME systemTime;
SECURITY_ATTRIBUTES saAttr = { 0 };
STRU struPath;
hr = UTILITY::ConvertPathToFullPath(
m_wsStdOutLogFileName.QueryStr(),
m_wsApplicationPath.QueryStr(),
&struPath);
if (FAILED(hr))
{
goto Finished;
}
hr = UTILITY::EnsureDirectoryPathExist(struPath.QueryStr());
if (FAILED(hr))
{
goto Finished;
}
m_fdStdOut = _dup(_fileno(stdout));
if (m_fdStdOut == -1)
{
hr = E_HANDLE;
goto Finished;
}
m_fdStdErr = _dup(_fileno(stderr));
if (m_fdStdErr == -1)
{
hr = E_HANDLE;
goto Finished;
}
GetSystemTime(&systemTime);
hr = m_struLogFilePath.SafeSnwprintf(L"%s_%d%02d%02d%02d%02d%02d_%d.log",
struPath.QueryStr(),
systemTime.wYear,
systemTime.wMonth,
systemTime.wDay,
systemTime.wHour,
systemTime.wMinute,
systemTime.wSecond,
GetCurrentProcessId());
if (FAILED(hr))
{
goto Finished;
}
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
if (_wfreopen_s(&m_pStdOutFile, m_struLogFilePath.QueryStr(), L"w+", stdout) == 0)
{
setvbuf(m_pStdOutFile, NULL, _IONBF, 0);
if (_dup2(_fileno(m_pStdOutFile), _fileno(stdout)) == -1)
{
hr = E_HANDLE;
goto Finished;
}
if (_dup2(_fileno(m_pStdOutFile), _fileno(stderr)) == -1)
{
hr = E_HANDLE;
goto Finished;
}
}
m_hLogFileHandle = (HANDLE)_get_osfhandle(_fileno(m_pStdOutFile));
if (m_hLogFileHandle == INVALID_HANDLE_VALUE)
{
hr = E_HANDLE;
goto Finished;
}
// Periodically flush the log content to file
m_Timer.InitializeTimer(STTIMER::TimerCallback, &m_struLogFilePath, FILE_FLUSH_TIMEOUT, FILE_FLUSH_TIMEOUT);
Finished:
return hr;
}

View File

@ -0,0 +1,31 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
#pragma once
class FileOutputManager : public IOutputManager
{
#define FILE_FLUSH_TIMEOUT 3000
public:
FileOutputManager();
~FileOutputManager();
HRESULT
Initialize(PCWSTR pwzStdOutLogFileName, PCWSTR pwzApplciationpath);
virtual bool GetStdOutContent(STRA* struStdOutput) override;
virtual HRESULT Start() override;
virtual void NotifyStartupComplete() override;
private:
HANDLE m_hLogFileHandle;
STTIMER m_Timer;
STRU m_wsStdOutLogFileName;
STRU m_wsApplicationPath;
STRU m_struLogFilePath;
int m_fdStdOut;
int m_fdStdErr;
FILE* m_pStdOutFile;
};

View File

@ -0,0 +1,25 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
#include "stdafx.h"
#pragma once
class IOutputManager
{
public:
virtual
HRESULT
Start() = 0;
virtual
~IOutputManager() {};
virtual
void
NotifyStartupComplete() = 0;
virtual
bool
GetStdOutContent(STRA* struStdOutput) = 0;
};

View File

@ -0,0 +1,35 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
#include "stdafx.h"
HRESULT
LoggingHelpers::CreateLoggingProvider(
bool fIsLoggingEnabled,
bool fEnablePipe,
PCWSTR pwzStdOutFileName,
PCWSTR pwzApplicationPath,
_Out_ IOutputManager** outputManager
)
{
HRESULT hr = S_OK;
DBG_ASSERT(outputManager != NULL);
if (fIsLoggingEnabled)
{
FileOutputManager* manager = new FileOutputManager;
hr = manager->Initialize(pwzStdOutFileName, pwzApplicationPath);
*outputManager = manager;
}
else if (fEnablePipe)
{
*outputManager = new PipeOutputManager;
}
else
{
*outputManager = new NullOutputManager;
}
return hr;
}

View File

@ -0,0 +1,20 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
#pragma once
class LoggingHelpers
{
public:
static
HRESULT
CreateLoggingProvider(
bool fLoggingEnabled,
bool fEnablePipe,
PCWSTR pwzStdOutFileName,
PCWSTR pwzApplicationPath,
_Out_ IOutputManager** outputManager
);
};

View File

@ -0,0 +1,31 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
#pragma once
#include "stdafx.h"
class NullOutputManager : public IOutputManager
{
public:
NullOutputManager() = default;
~NullOutputManager() = default;
HRESULT Start()
{
// The process has console, e.g., IIS Express scenario
return S_OK;
}
void NotifyStartupComplete()
{
}
bool GetStdOutContent(STRA*)
{
return false;
}
};

View File

@ -0,0 +1,196 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
#include "stdafx.h"
PipeOutputManager::PipeOutputManager() :
m_dwStdErrReadTotal(0),
m_hErrReadPipe(INVALID_HANDLE_VALUE),
m_hErrWritePipe(INVALID_HANDLE_VALUE),
m_hErrThread(INVALID_HANDLE_VALUE),
m_fDisposed(FALSE)
{
}
PipeOutputManager::~PipeOutputManager()
{
StopOutputRedirection();
}
VOID
PipeOutputManager::StopOutputRedirection()
{
DWORD dwThreadStatus = 0;
STRA straStdOutput;
if (m_fDisposed)
{
return;
}
m_fDisposed = true;
fflush(stdout);
fflush(stderr);
if (m_hErrWritePipe != INVALID_HANDLE_VALUE)
{
CloseHandle(m_hErrWritePipe);
m_hErrWritePipe = INVALID_HANDLE_VALUE;
}
if (m_hErrThread != NULL &&
GetExitCodeThread(m_hErrThread, &dwThreadStatus) != 0 &&
dwThreadStatus == STILL_ACTIVE)
{
// wait for gracefullshut down, i.e., the exit of the background thread or timeout
if (WaitForSingleObject(m_hErrThread, PIPE_OUTPUT_THREAD_TIMEOUT) != WAIT_OBJECT_0)
{
// if the thread is still running, we need kill it first before exit to avoid AV
if (GetExitCodeThread(m_hErrThread, &dwThreadStatus) != 0 && dwThreadStatus == STILL_ACTIVE)
{
TerminateThread(m_hErrThread, STATUS_CONTROL_C_EXIT);
}
}
}
CloseHandle(m_hErrThread);
m_hErrThread = NULL;
if (m_hErrReadPipe != INVALID_HANDLE_VALUE)
{
CloseHandle(m_hErrReadPipe);
m_hErrReadPipe = INVALID_HANDLE_VALUE;
}
// Restore the original stdout and stderr handles of the process,
// as the application has either finished startup or has exited.
if (_dup2(m_fdStdOut, _fileno(stdout)) == -1)
{
return;
}
if (_dup2(m_fdStdErr, _fileno(stderr)) == -1)
{
return;
}
if (GetStdOutContent(&straStdOutput))
{
printf(straStdOutput.QueryStr());
// Need to flush contents.
_flushall();
}
}
HRESULT PipeOutputManager::Start()
{
HRESULT hr = S_OK;
SECURITY_ATTRIBUTES saAttr = { 0 };
HANDLE hStdErrReadPipe;
HANDLE hStdErrWritePipe;
m_fdStdOut = _dup(_fileno(stdout));
if (m_fdStdOut == -1)
{
hr = E_HANDLE;
goto Finished;
}
m_fdStdErr = _dup(_fileno(stderr));
if (m_fdStdErr == -1)
{
hr = E_HANDLE;
goto Finished;
}
if (!CreatePipe(&hStdErrReadPipe, &hStdErrWritePipe, &saAttr, 0 /*nSize*/))
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto Finished;
}
// TODO this still doesn't redirect calls in native, like wprintf
if (!SetStdHandle(STD_ERROR_HANDLE, hStdErrWritePipe))
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto Finished;
}
if (!SetStdHandle(STD_OUTPUT_HANDLE, hStdErrWritePipe))
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto Finished;
}
m_hErrReadPipe = hStdErrReadPipe;
m_hErrWritePipe = hStdErrWritePipe;
// Read the stderr handle on a separate thread until we get 4096 bytes.
m_hErrThread = CreateThread(
NULL, // default security attributes
0, // default stack size
(LPTHREAD_START_ROUTINE)ReadStdErrHandle,
this, // thread function arguments
0, // default creation flags
NULL); // receive thread identifier
if (m_hErrThread == NULL)
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto Finished;
}
Finished:
return hr;
}
VOID
PipeOutputManager::ReadStdErrHandle(
LPVOID pContext
)
{
PipeOutputManager *pLoggingProvider = (PipeOutputManager*)pContext;
DBG_ASSERT(pLoggingProvider != NULL);
pLoggingProvider->ReadStdErrHandleInternal();
}
bool PipeOutputManager::GetStdOutContent(STRA* struStdOutput)
{
bool fLogged = false;
if (m_dwStdErrReadTotal > 0)
{
if (SUCCEEDED(struStdOutput->Copy(m_pzFileContents, m_dwStdErrReadTotal)))
{
fLogged = TRUE;
}
}
return fLogged;
}
VOID
PipeOutputManager::ReadStdErrHandleInternal(
VOID
)
{
DWORD dwNumBytesRead = 0;
while (true)
{
if (ReadFile(m_hErrReadPipe, &m_pzFileContents[m_dwStdErrReadTotal], MAX_PIPE_READ_SIZE - m_dwStdErrReadTotal, &dwNumBytesRead, NULL))
{
m_dwStdErrReadTotal += dwNumBytesRead;
if (m_dwStdErrReadTotal >= MAX_PIPE_READ_SIZE)
{
break;
}
}
else if (GetLastError() == ERROR_BROKEN_PIPE)
{
break;
}
}
}
VOID
PipeOutputManager::NotifyStartupComplete()
{
StopOutputRedirection();
}

View File

@ -0,0 +1,38 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
#pragma once
class PipeOutputManager : public IOutputManager
{
#define PIPE_OUTPUT_THREAD_TIMEOUT 2000
#define MAX_PIPE_READ_SIZE 4096
public:
PipeOutputManager();
~PipeOutputManager();
// Inherited via ILoggerProvider
virtual HRESULT Start() override;
virtual void NotifyStartupComplete() override;
// Inherited via IOutputManager
virtual bool GetStdOutContent(STRA* struStdOutput) override;
VOID ReadStdErrHandleInternal(VOID);
static
VOID ReadStdErrHandle(LPVOID pContext);
VOID StopOutputRedirection();
private:
HANDLE m_hErrReadPipe;
HANDLE m_hErrWritePipe;
STRU m_struLogFilePath;
HANDLE m_hErrThread;
CHAR m_pzFileContents[MAX_PIPE_READ_SIZE] = { 0 };
BOOL m_fDisposed;
DWORD m_dwStdErrReadTotal;
int m_fdStdOut;
int m_fdStdErr;
};

View File

@ -1,3 +1,6 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
#include "stdafx.h"
#include "SRWLockWrapper.h"

View File

@ -1,4 +1,8 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
#pragma once
class SRWLockWrapper
{
public:

View File

@ -15,6 +15,7 @@
#include <sstream>
#include <memory>
#include "Shlwapi.h"
#include <io.h>
#include "hashtable.h"
#include "stringu.h"
#include "stringa.h"
@ -23,6 +24,7 @@
#include "ahutil.h"
#include "hashfn.h"
#include "irequesthandler.h"
#include "sttimer.h"
#include "requesthandler.h"
#include "iapplication.h"
#include "application.h"
@ -34,3 +36,8 @@
#include "fx_ver.h"
#include "hostfxr_utility.h"
#include "hostfxroptions.h"
#include "IOutputManager.h"
#include "FileOutputManager.h"
#include "PipeOutputManager.h"
#include "NullOutputManager.h"
#include "LoggingHelpers.h"

View File

@ -214,7 +214,6 @@
<ClInclude Include="disconnectcontext.h" />
<ClInclude Include="environmentvariablehelpers.h" />
<ClInclude Include="precomp.hxx" />
<ClInclude Include="sttimer.h" />
<ClInclude Include=".\inprocess\inprocessapplication.h" />
<ClInclude Include=".\inprocess\inprocesshandler.h" />
</ItemGroup>

View File

@ -10,11 +10,6 @@ IN_PROCESS_APPLICATION::IN_PROCESS_APPLICATION(
PCWSTR pDotnetExeLocation) :
m_pHttpServer(pHttpServer),
m_ProcessExitCode(0),
m_hLogFileHandle(INVALID_HANDLE_VALUE),
m_hErrReadPipe(INVALID_HANDLE_VALUE),
m_hErrWritePipe(INVALID_HANDLE_VALUE),
m_dwStdErrReadTotal(0),
m_fDoneStdRedirect(FALSE),
m_fBlockCallbacksIntoManaged(FALSE),
m_fInitialized(FALSE),
m_fShutdownCalledFromNative(FALSE),
@ -35,12 +30,6 @@ IN_PROCESS_APPLICATION::IN_PROCESS_APPLICATION(
IN_PROCESS_APPLICATION::~IN_PROCESS_APPLICATION()
{
if (m_hLogFileHandle != INVALID_HANDLE_VALUE)
{
m_Timer.CancelTimer();
CloseHandle(m_hLogFileHandle);
m_hLogFileHandle = INVALID_HANDLE_VALUE;
}
if (m_pConfig != NULL)
{
@ -106,6 +95,12 @@ IN_PROCESS_APPLICATION::ShutDown(
}
}
if (m_pLoggerProvider != NULL)
{
delete m_pLoggerProvider;
m_pLoggerProvider = NULL;
}
Finished:
if (FAILED(hr))
@ -131,8 +126,6 @@ IN_PROCESS_APPLICATION::ShutDownInternal()
{
DWORD dwThreadStatus = 0;
DWORD dwTimeout = m_pConfig->QueryShutdownTimeLimitInMS();
HANDLE handle = NULL;
WIN32_FIND_DATA fileData;
if (IsDebuggerPresent())
{
@ -201,34 +194,6 @@ IN_PROCESS_APPLICATION::ShutDownInternal()
CloseHandle(m_hThread);
m_hThread = NULL;
s_Application = NULL;
CloseStdErrHandles();
if (m_pStdFile != NULL)
{
fflush(stdout);
fflush(stderr);
fclose(m_pStdFile);
}
if (m_hLogFileHandle != INVALID_HANDLE_VALUE)
{
m_Timer.CancelTimer();
CloseHandle(m_hLogFileHandle);
m_hLogFileHandle = INVALID_HANDLE_VALUE;
}
// delete empty log file
handle = FindFirstFile(m_struLogFilePath.QueryStr(), &fileData);
if (handle != INVALID_HANDLE_VALUE &&
fileData.nFileSizeHigh == 0 &&
fileData.nFileSizeLow == 0) // skip check of nFileSizeHigh
{
FindClose(handle);
// no need to check whether the deletion succeeds
// as nothing can be done
DeleteFile(m_struLogFilePath.QueryStr());
}
}
__override
@ -371,263 +336,13 @@ IN_PROCESS_APPLICATION::SetCallbackHandles(
m_ShutdownHandlerContext = pvShutdownHandlerContext;
m_AsyncCompletionHandler = async_completion_handler;
CloseStdErrHandles();
m_pLoggerProvider->NotifyStartupComplete();
// Can't check the std err handle as it isn't a critical error
SetStdHandle(STD_ERROR_HANDLE, INVALID_HANDLE_VALUE);
// Initialization complete
SetEvent(m_pInitalizeEvent);
m_fInitialized = TRUE;
}
VOID
IN_PROCESS_APPLICATION::SetStdOut(
VOID
)
{
HRESULT hr = S_OK;
STRU struPath;
SYSTEMTIME systemTime;
SECURITY_ATTRIBUTES saAttr = { 0 };
HANDLE hStdErrReadPipe;
HANDLE hStdErrWritePipe;
if (!m_fDoneStdRedirect)
{
// Have not set stdout yet, redirect stdout to log file
SRWLockWrapper lock(m_srwLock);
if (!m_fDoneStdRedirect)
{
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
//
// best effort
// no need to capture the error code as nothing we can do here
// in case mamanged layer exits abnormally, may not be able to capture the log content as it is buffered.
//
if (!GetConsoleWindow())
{
// Full IIS scenario.
//
// SetStdHandle works as w3wp does not have Console
// Current process does not have a console
//
if (m_pConfig->QueryStdoutLogEnabled())
{
hr = UTILITY::ConvertPathToFullPath(
m_pConfig->QueryStdoutLogFile()->QueryStr(),
m_pConfig->QueryApplicationPhysicalPath()->QueryStr(),
&struPath);
if (FAILED(hr))
{
goto Finished;
}
hr = UTILITY::EnsureDirectoryPathExist(struPath.QueryStr());
if (FAILED(hr))
{
goto Finished;
}
GetSystemTime(&systemTime);
hr = m_struLogFilePath.SafeSnwprintf(L"%s_%d%02d%02d%02d%02d%02d_%d.log",
struPath.QueryStr(),
systemTime.wYear,
systemTime.wMonth,
systemTime.wDay,
systemTime.wHour,
systemTime.wMinute,
systemTime.wSecond,
GetCurrentProcessId());
if (FAILED(hr))
{
goto Finished;
}
m_hLogFileHandle = CreateFileW(m_struLogFilePath.QueryStr(),
FILE_READ_DATA | FILE_WRITE_DATA,
FILE_SHARE_READ,
&saAttr,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (m_hLogFileHandle == INVALID_HANDLE_VALUE)
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto Finished;
}
if (!SetStdHandle(STD_OUTPUT_HANDLE, m_hLogFileHandle))
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto Finished;
}
if (!SetStdHandle(STD_ERROR_HANDLE, m_hLogFileHandle))
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto Finished;
}
// not work
// AllocConsole() does not help
// *stdout = *m_pStdFile;
// *stderr = *m_pStdFile;
// _dup2(_fileno(m_pStdFile), _fileno(stdout));
// _dup2(_fileno(m_pStdFile), _fileno(stderr));
// this one cannot capture the process start failure
// _wfreopen_s(&m_pStdFile, struLogFileName.QueryStr(), L"w", stdout);
// Periodically flush the log content to file
m_Timer.InitializeTimer(STTIMER::TimerCallback, &m_struLogFilePath, 3000, 3000);
}
else
{
//
// CreatePipe for outputting stderr to the windows event log.
// Ignore failures
//
if (!CreatePipe(&hStdErrReadPipe, &hStdErrWritePipe, &saAttr, 0 /*nSize*/))
{
goto Finished;
}
if (!SetStdHandle(STD_ERROR_HANDLE, hStdErrWritePipe))
{
goto Finished;
}
m_hErrReadPipe = hStdErrReadPipe;
m_hErrWritePipe = hStdErrWritePipe;
// Read the stderr handle on a separate thread until we get 4096 bytes.
m_hErrThread = CreateThread(
NULL, // default security attributes
0, // default stack size
(LPTHREAD_START_ROUTINE)ReadStdErrHandle,
this, // thread function arguments
0, // default creation flags
NULL); // receive thread identifier
if (m_hErrThread == NULL)
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto Finished;
}
}
}
else
{
// The process has console, e.g., IIS Express scenario
if (_wfopen_s(&m_pStdFile, m_struLogFilePath.QueryStr(), L"w") == 0)
{
// known issue: error info may not be capture when process crashes during buffering
// even we disabled FILE buffering
setvbuf(m_pStdFile, NULL, _IONBF, 0);
_dup2(_fileno(m_pStdFile), _fileno(stdout));
_dup2(_fileno(m_pStdFile), _fileno(stderr));
}
// These don't work for console scenario
// close and AllocConsole does not help
//_wfreopen_s(&m_pStdFile, struLogFileName.QueryStr(), L"w", stdout);
// SetStdHandle(STD_ERROR_HANDLE, m_hLogFileHandle);
// SetStdHandle(STD_OUTPUT_HANDLE, m_hLogFileHandle);
//*stdout = *m_pStdFile;
//*stderr = *m_pStdFile;
}
}
}
Finished:
m_fDoneStdRedirect = TRUE;
if (FAILED(hr) && m_pConfig->QueryStdoutLogEnabled())
{
UTILITY::LogEventF(g_hEventLog,
EVENTLOG_WARNING_TYPE,
ASPNETCORE_EVENT_CONFIG_ERROR,
ASPNETCORE_EVENT_INVALID_STDOUT_LOG_FILE_MSG,
m_struLogFilePath.QueryStr(),
hr);
}
}
VOID
IN_PROCESS_APPLICATION::ReadStdErrHandle(
LPVOID pContext
)
{
IN_PROCESS_APPLICATION *pApplication = (IN_PROCESS_APPLICATION*)pContext;
DBG_ASSERT(pApplication != NULL);
pApplication->ReadStdErrHandleInternal();
}
VOID
IN_PROCESS_APPLICATION::ReadStdErrHandleInternal(
VOID
)
{
DWORD dwNumBytesRead = 0;
while (true)
{
if (ReadFile(m_hErrReadPipe, &m_pzFileContents[m_dwStdErrReadTotal], 4096 - m_dwStdErrReadTotal, &dwNumBytesRead, NULL))
{
m_dwStdErrReadTotal += dwNumBytesRead;
if (m_dwStdErrReadTotal >= 4096)
{
break;
}
}
else if (GetLastError() == ERROR_BROKEN_PIPE)
{
break;
}
}
}
VOID
IN_PROCESS_APPLICATION::CloseStdErrHandles
(
VOID
)
{
DWORD dwThreadStatus = 0;
DWORD dwTimeout = m_pConfig->QueryShutdownTimeLimitInMS();
// Close Handles for stderr as we only care about capturing startup errors
if (m_hErrWritePipe != INVALID_HANDLE_VALUE)
{
CloseHandle(m_hErrWritePipe);
m_hErrWritePipe = INVALID_HANDLE_VALUE;
}
if (m_hErrThread != NULL &&
GetExitCodeThread(m_hErrThread, &dwThreadStatus) != 0 &&
dwThreadStatus == STILL_ACTIVE)
{
// wait for gracefullshut down, i.e., the exit of the background thread or timeout
if (WaitForSingleObject(m_hErrThread, dwTimeout) != WAIT_OBJECT_0)
{
// if the thread is still running, we need kill it first before exit to avoid AV
if (GetExitCodeThread(m_hErrThread, &dwThreadStatus) != 0 && dwThreadStatus == STILL_ACTIVE)
{
TerminateThread(m_hErrThread, STATUS_CONTROL_C_EXIT);
}
}
}
CloseHandle(m_hErrThread);
m_hErrThread = NULL;
if (m_hErrReadPipe != INVALID_HANDLE_VALUE)
{
CloseHandle(m_hErrReadPipe);
m_hErrReadPipe = INVALID_HANDLE_VALUE;
}
}
// Will be called by the inprocesshandler
HRESULT
IN_PROCESS_APPLICATION::LoadManagedApplication
@ -657,11 +372,27 @@ IN_PROCESS_APPLICATION::LoadManagedApplication
goto Finished;
}
// Set up stdout redirect
SetStdOut();
{
// Set up stdout redirect
SRWLockWrapper lock(m_srwLock);
if (m_pLoggerProvider == NULL)
{
hr = LoggingHelpers::CreateLoggingProvider(
m_pConfig->QueryStdoutLogEnabled(),
!GetConsoleWindow(),
m_pConfig->QueryStdoutLogFile()->QueryStr(),
m_pConfig->QueryApplicationPhysicalPath()->QueryStr(),
&m_pLoggerProvider);
if (FAILED(hr))
{
goto Finished;
}
m_pLoggerProvider->Start();
}
if (m_status != APPLICATION_STATUS::STARTING)
{
if (m_status == APPLICATION_STATUS::FAIL)
@ -887,6 +618,8 @@ Finished:
if (!m_fShutdownCalledFromNative)
{
m_pLoggerProvider->NotifyStartupComplete();
LogErrorsOnMainExit(hr);
if (m_fInitialized)
{
@ -910,72 +643,20 @@ IN_PROCESS_APPLICATION::LogErrorsOnMainExit(
// This will be a common place for errors as it means the hostfxr_main returned
// or there was an exception.
//
CHAR pzFileContents[4096] = { 0 };
DWORD dwNumBytesRead;
STRU struStdErrLog;
LARGE_INTEGER li = { 0 };
BOOL fLogged = FALSE;
DWORD dwFilePointer = 0;
if (m_pConfig->QueryStdoutLogEnabled())
STRA struStdErrOutput;
if (m_pLoggerProvider->GetStdOutContent(&struStdErrOutput))
{
// Put stdout/stderr logs into
if (m_hLogFileHandle != INVALID_HANDLE_VALUE)
{
if (GetFileSizeEx(m_hLogFileHandle, &li) && li.LowPart > 0 && li.HighPart == 0)
{
if (li.LowPart > 4096)
{
dwFilePointer = SetFilePointer(m_hLogFileHandle, -4096, NULL, FILE_END);
}
else
{
dwFilePointer = SetFilePointer(m_hLogFileHandle, 0, NULL, FILE_BEGIN);
}
if (dwFilePointer != INVALID_SET_FILE_POINTER)
{
if (ReadFile(m_hLogFileHandle, pzFileContents, 4096, &dwNumBytesRead, NULL))
{
if (SUCCEEDED(struStdErrLog.CopyA(m_pzFileContents, m_dwStdErrReadTotal)))
{
UTILITY::LogEventF(g_hEventLog,
EVENTLOG_ERROR_TYPE,
ASPNETCORE_EVENT_INPROCESS_THREAD_EXIT,
ASPNETCORE_EVENT_INPROCESS_THREAD_EXIT_STDOUT_MSG,
m_pConfig->QueryApplicationPath()->QueryStr(),
m_pConfig->QueryApplicationPhysicalPath()->QueryStr(),
hr,
struStdErrLog.QueryStr());
fLogged = TRUE;
}
}
}
}
}
UTILITY::LogEventF(g_hEventLog,
EVENTLOG_ERROR_TYPE,
ASPNETCORE_EVENT_INPROCESS_THREAD_EXIT,
ASPNETCORE_EVENT_INPROCESS_THREAD_EXIT_STDOUT_MSG,
m_pConfig->QueryApplicationPath()->QueryStr(),
m_pConfig->QueryApplicationPhysicalPath()->QueryStr(),
hr,
struStdErrOutput.QueryStr());
}
else
{
if (m_dwStdErrReadTotal > 0)
{
if (SUCCEEDED(struStdErrLog.CopyA(m_pzFileContents, m_dwStdErrReadTotal)))
{
UTILITY::LogEventF(g_hEventLog,
EVENTLOG_ERROR_TYPE,
ASPNETCORE_EVENT_INPROCESS_THREAD_EXIT,
ASPNETCORE_EVENT_INPROCESS_THREAD_EXIT_STDERR_MSG,
m_pConfig->QueryApplicationPath()->QueryStr(),
m_pConfig->QueryApplicationPhysicalPath()->QueryStr(),
hr,
struStdErrLog.QueryStr());
fLogged = TRUE;
}
}
}
if (!fLogged)
{
// If we didn't log, log the generic message.
UTILITY::LogEventF(g_hEventLog,
EVENTLOG_ERROR_TYPE,
ASPNETCORE_EVENT_INPROCESS_THREAD_EXIT,
@ -983,41 +664,34 @@ IN_PROCESS_APPLICATION::LogErrorsOnMainExit(
m_pConfig->QueryApplicationPath()->QueryStr(),
m_pConfig->QueryApplicationPhysicalPath()->QueryStr(),
hr);
fLogged = TRUE;
}
}
//
// Calls hostfxr_main with the hostfxr and application as arguments.
// Method should be called with only
// Need to have __try / __except in methods that require unwinding.
// Note, this will not
//
HRESULT
IN_PROCESS_APPLICATION::RunDotnetApplication(DWORD argc, CONST PCWSTR* argv, hostfxr_main_fn pProc)
{
HRESULT hr = S_OK;
__try
{
m_ProcessExitCode = pProc(argc, argv);
if (m_ProcessExitCode != 0)
{
hr = HRESULT_FROM_WIN32(GetLastError());
}
}
__except (FilterException(GetExceptionCode(), GetExceptionInformation()))
__except(GetExceptionCode() != 0)
{
// TODO Log error message here.
hr = E_APPLICATION_ACTIVATION_EXEC_FAILURE;
hr = HRESULT_FROM_WIN32(GetLastError());
}
return hr;
}
// static
INT
IN_PROCESS_APPLICATION::FilterException(unsigned int, struct _EXCEPTION_POINTERS*)
{
// We assume that any exception is a failure as the applicaiton didn't start or there was a startup error.
// TODO, log error based on exception code.
return EXCEPTION_EXECUTE_HANDLER;
}
REQUESTHANDLER_CONFIG*
IN_PROCESS_APPLICATION::QueryConfig() const

View File

@ -55,16 +55,6 @@ public:
VOID
);
VOID
ReadStdErrHandleInternal(
VOID
);
VOID
CloseStdErrHandles(
VOID
);
HRESULT
LoadManagedApplication(
VOID
@ -130,17 +120,6 @@ public:
}
private:
static
DWORD
DoShutDown(
LPVOID lpParam
);
VOID
ShutDownInternal(
VOID
);
IHttpServer* const m_pHttpServer;
// Thread executing the .NET Core process
@ -159,26 +138,18 @@ private:
// The event that gets triggered when managed initialization is complete
HANDLE m_pInitalizeEvent;
// The std log file handle
HANDLE m_hLogFileHandle;
HANDLE m_hErrReadPipe;
HANDLE m_hErrWritePipe;
STRU m_struLogFilePath;
STRU m_struExeLocation;
// The exit code of the .NET Core process
INT m_ProcessExitCode;
BOOL m_fIsWebSocketsConnection;
BOOL m_fDoneStdRedirect;
volatile BOOL m_fBlockCallbacksIntoManaged;
volatile BOOL m_fShutdownCalledFromNative;
volatile BOOL m_fShutdownCalledFromManaged;
BOOL m_fRecycleCalled;
BOOL m_fInitialized;
FILE* m_pStdFile;
STTIMER m_Timer;
SRWLOCK m_srwLock;
// Thread for capturing startup stderr logs when logging is disabled
@ -188,6 +159,7 @@ private:
PCWSTR m_pstrDotnetExeLocation;
static IN_PROCESS_APPLICATION* s_Application;
IOutputManager* m_pLoggerProvider;
REQUESTHANDLER_CONFIG* m_pConfig;
// Allows to override call to hostfxr_main with custome callback
@ -205,26 +177,26 @@ private:
_In_ LPVOID pContext
);
static
VOID
ReadStdErrHandle
(
_In_ LPVOID pContext
);
HRESULT
SetEnvironementVariablesOnWorkerProcess(
VOID
);
static
INT
FilterException(unsigned int code, struct _EXCEPTION_POINTERS *ep);
HRESULT
RunDotnetApplication(
DWORD argc,
CONST PCWSTR* argv,
hostfxr_main_fn pProc
);
static
DWORD
DoShutDown(
LPVOID lpParam
);
VOID
ShutDownInternal(
VOID
);
};

View File

@ -217,7 +217,6 @@
<ClInclude Include="aspnetcore_event.h" />
<ClInclude Include="disconnectcontext.h" />
<ClInclude Include="environmentvariablehelpers.h" />
<ClInclude Include="sttimer.h" />
<ClInclude Include="outofprocess\forwarderconnection.h" />
<ClInclude Include="outofprocess\processmanager.h" />
<ClInclude Include="outofprocess\protocolconfig.h" />

View File

@ -32,12 +32,28 @@
<ImportGroup Label="Shared" />
<ImportGroup Label="PropertySheets" />
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<OutDir>$(MSBuildProjectDirectory)\bin\$(Configuration)\$(Platform)\</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<OutDir>$(MSBuildProjectDirectory)\bin\$(Configuration)\$(Platform)\</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<OutDir>$(MSBuildProjectDirectory)\bin\$(Configuration)\$(Platform)\</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<OutDir>$(MSBuildProjectDirectory)\bin\$(Configuration)\$(Platform)\</OutDir>
</PropertyGroup>
<ItemGroup>
<ClInclude Include="Helpers.h" />
<ClInclude Include="stdafx.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="FileOutputManagerTests.cpp" />
<ClCompile Include="Helpers.cpp" />
<ClCompile Include="hostfxr_utility_tests.cpp" />
<ClCompile Include="main.cpp" />
<ClCompile Include="PipeOutputManagerTests.cpp" />
<ClCompile Include="stdafx.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
@ -141,7 +157,7 @@
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<AdditionalIncludeDirectories>$(MSBuildThisFileDirectory)include;%(AdditionalIncludeDirectories);..\..\src\AspNetCoreModuleV2\IISLib;..\..\src\AspNetCoreModuleV2\CommonLib;..\gtest-1.8.0\include;..\..\src\AspNetCoreModuleV2\AspNetCore\Inc;</AdditionalIncludeDirectories>
<AdditionalOptions>/D "_SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING" </AdditionalOptions>
<LanguageStandard>stdcpp17</LanguageStandard>
<LanguageStandard>stdcpp14</LanguageStandard>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>

View File

@ -0,0 +1,142 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
#include "stdafx.h"
#include "gtest/internal/gtest-port.h"
class FileManagerWrapper
{
public:
FileOutputManager* manager;
FileManagerWrapper(FileOutputManager* m)
: manager(m)
{
manager->Start();
}
~FileManagerWrapper()
{
delete manager;
}
};
namespace FileOutManagerStartupTests
{
using ::testing::Test;
class FileOutputManagerTest : public Test
{
protected:
void
Test(std::wstring fileNamePrefix, FILE* out)
{
PCWSTR expected = L"test";
std::wstring tempDirectory = Helpers::CreateRandomTempDirectory();
FileOutputManager* pManager = new FileOutputManager;
pManager->Initialize(fileNamePrefix.c_str(), tempDirectory.c_str());
{
FileManagerWrapper wrapper(pManager);
wprintf(expected, out);
}
// std::filesystem is available on c++17, however gtest fails to build when using it
// c++14 has filesystem as experimental.
for (auto & p : std::experimental::filesystem::directory_iterator(tempDirectory))
{
std::wstring filename(p.path().filename());
ASSERT_EQ(filename.substr(0, fileNamePrefix.size()), fileNamePrefix);
std::wstring content = Helpers::ReadFileContent(std::wstring(p.path()));
ASSERT_EQ(content.length(), DWORD(4));
ASSERT_STREQ(content.c_str(), expected);
}
Helpers::DeleteDirectory(tempDirectory);
}
};
TEST_F(FileOutputManagerTest, WriteToFileCheckContentsWritten)
{
Test(L"", stdout);
Test(L"log", stdout);
}
TEST_F(FileOutputManagerTest, WriteToFileCheckContentsWrittenErr)
{
Test(L"", stderr);
Test(L"log", stderr);
}
}
namespace FileOutManagerOutputTests
{
TEST(FileOutManagerOutputTest, StdErr)
{
PCSTR expected = "test";
std::wstring tempDirectory = Helpers::CreateRandomTempDirectory();
FileOutputManager* pManager = new FileOutputManager;
pManager->Initialize(L"", tempDirectory.c_str());
{
FileManagerWrapper wrapper(pManager);
printf(expected, stderr);
STRA straContent;
ASSERT_TRUE(pManager->GetStdOutContent(&straContent));
ASSERT_STREQ(straContent.QueryStr(), expected);
}
Helpers::DeleteDirectory(tempDirectory);
}
TEST(FileOutManagerOutputTest, CheckFileOutput)
{
PCSTR expected = "test";
std::wstring tempDirectory = Helpers::CreateRandomTempDirectory();
FileOutputManager* pManager = new FileOutputManager;
pManager->Initialize(L"", tempDirectory.c_str());
{
FileManagerWrapper wrapper(pManager);
printf(expected);
STRA straContent;
ASSERT_TRUE(pManager->GetStdOutContent(&straContent));
ASSERT_STREQ(straContent.QueryStr(), expected);
}
Helpers::DeleteDirectory(tempDirectory);
}
TEST(FileOutManagerOutputTest, CapAt4KB)
{
PCSTR expected = "test";
std::wstring tempDirectory = Helpers::CreateRandomTempDirectory();
FileOutputManager* pManager = new FileOutputManager;
pManager->Initialize(L"", tempDirectory.c_str());
{
FileManagerWrapper wrapper(pManager);
for (int i = 0; i < 1200; i++)
{
printf(expected);
}
STRA straContent;
ASSERT_TRUE(pManager->GetStdOutContent(&straContent));
ASSERT_EQ(straContent.QueryCCH(), 4096);
}
Helpers::DeleteDirectory(tempDirectory);
}
}

View File

@ -0,0 +1,48 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
#include "stdafx.h"
std::wstring
Helpers::CreateRandomValue()
{
int randomValue = rand();
return std::to_wstring(randomValue);
}
std::wstring
Helpers::CreateRandomTempDirectory()
{
PWSTR tempPath = new WCHAR[256];
GetTempPath(256, tempPath);
std::wstring wstringPath(tempPath);
return wstringPath.append(Helpers::CreateRandomValue()).append(L"\\");
}
void
Helpers::DeleteDirectory(std::wstring directory)
{
std::experimental::filesystem::remove_all(directory);
}
std::wstring
Helpers::ReadFileContent(std::wstring file)
{
std::wcout << file << std::endl;
std::fstream t(file);
std::stringstream buffer;
buffer << t.rdbuf();
int nChars = MultiByteToWideChar(CP_ACP, 0, buffer.str().c_str(), -1, NULL, 0);
LPWSTR pwzName = new WCHAR[nChars];
MultiByteToWideChar(CP_UTF8, 0, buffer.str().c_str(), -1, pwzName, nChars);
std::wstring retVal(pwzName);
delete pwzName;
return retVal;
}

View File

@ -0,0 +1,25 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
#pragma once
class Helpers
{
public:
static
std::wstring
CreateRandomValue();
static
std::wstring
CreateRandomTempDirectory();
static
void
DeleteDirectory(std::wstring directory);
static
std::wstring
ReadFileContent(std::wstring file);
};

View File

@ -0,0 +1,39 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
#include "stdafx.h"
#include "gtest/internal/gtest-port.h"
class FileManagerWrapper
{
public:
PipeOutputManager * manager;
FileManagerWrapper(PipeOutputManager* m)
: manager(m)
{
manager->Start();
}
~FileManagerWrapper()
{
delete manager;
}
};
namespace PipeOutputManagerTests
{
TEST(PipeManagerOutputTest, NotifyStartupCompleteCallsDispose)
{
PCWSTR expected = L"test";
PipeOutputManager* pManager = new PipeOutputManager();
ASSERT_EQ(S_OK, pManager->Start());
pManager->NotifyStartupComplete();
// Test will fail if we didn't redirect stdout back to a file descriptor.
// This is because gtest relies on console output to know if a test succeeded or failed.
// If the output still points to a file/pipe, the test (and all other tests after it) will fail.
}
}

View File

@ -9,6 +9,7 @@ int wmain(int argc, wchar_t* argv[])
int main(int argc, char* argv[])
#endif
{
std::srand((unsigned int)std::time(0));
::testing::InitGoogleTest(&argc, argv);
RUN_ALL_TESTS();
return 0;

View File

@ -17,6 +17,8 @@
#include <wchar.h>
#include <io.h>
#include <stdio.h>
#include <experimental/filesystem>
#include <fstream>
#include <hashfn.h>
#include <hashtable.h>
@ -48,6 +50,7 @@
#include "requesthandler.h"
#include "resources.h"
#include "aspnetcore_msg.h"
#include "Helpers.h"
#undef assert // Macro redefinition in IISLib.
#include "gtest\gtest.h"

View File

@ -0,0 +1,70 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using IISIntegration.FunctionalTests.Utilities;
using Microsoft.AspNetCore.Server.IntegrationTesting;
using Microsoft.Extensions.Logging;
using Xunit;
namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
{
public class LoggingTests : IISFunctionalTestBase
{
[Theory]
[InlineData("CheckErrLogFile")]
[InlineData("CheckLogFile")]
public async Task CheckStdoutLogging(string path)
{
var deploymentParameters = Helpers.GetBaseDeploymentParameters();
deploymentParameters.PublishApplicationBeforeDeployment = true;
deploymentParameters.PreservePublishedApplicationForDebugging = true; // workaround for keeping
var deploymentResult = await DeployAsync(deploymentParameters);
Helpers.ModifyAspNetCoreSectionInWebConfig(deploymentResult, "stdoutLogEnabled", "true");
Helpers.ModifyAspNetCoreSectionInWebConfig(deploymentResult, "stdoutLogFile", @".\logs\stdout");
var response = await deploymentResult.RetryingHttpClient.GetAsync(path);
var responseText = await response.Content.ReadAsStringAsync();
Assert.Equal("Hello World", responseText);
Dispose();
var folderPath = Path.Combine(deploymentResult.DeploymentResult.ContentRoot, @"logs");
var fileInDirectory = Directory.GetFiles(folderPath).Single();
Assert.NotNull(fileInDirectory);
string contents = null;
for (var i = 0; i < 20; i++)
{
try
{
contents = await File.ReadAllTextAsync(fileInDirectory);
break;
}
catch (IOException)
{
// File in use by IISExpress, delay a bit before rereading.
}
await Task.Delay(20);
}
Assert.NotNull(contents);
// Open the log file and see if there are any contents.
Assert.Contains("TEST MESSAGE", contents);
RetryHelper.RetryOperation(
() => Directory.Delete(deploymentParameters.PublishedApplicationRootPath, true),
e => Logger.LogWarning($"Failed to delete directory : {e.Message}"),
retryCount: 3,
retryDelayMilliseconds: 100);
}
}
}

View File

@ -0,0 +1,52 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Threading.Tasks;
using IISIntegration.FunctionalTests.Utilities;
using Xunit;
using System.Net;
namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
{
public class StartupExceptionTests : IISFunctionalTestBase
{
[Theory]
[InlineData("CheckLogFile")]
[InlineData("CheckErrLogFile")]
public async Task CheckStdoutWithRandomNumber(string path)
{
var deploymentParameters = Helpers.GetBaseDeploymentParameters("StartupExceptionWebsite");
deploymentParameters.EnvironmentVariables["ASPNETCORE_INPROCESS_STARTUP_VALUE"] = path;
var randomNumberString = new Random(Guid.NewGuid().GetHashCode()).Next(10000000).ToString();
deploymentParameters.EnvironmentVariables["ASPNETCORE_INPROCESS_RANDOM_VALUE"] = randomNumberString;
var deploymentResult = await DeployAsync(deploymentParameters);
var response = await deploymentResult.RetryingHttpClient.GetAsync(path);
Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode);
Assert.Contains(TestSink.Writes, context => context.Message.Contains($"Random number: {randomNumberString}"));
}
[Theory]
[InlineData("CheckLargeStdErrWrites")]
[InlineData("CheckLargeStdOutWrites")]
[InlineData("CheckOversizedStdErrWrites")]
[InlineData("CheckOversizedStdOutWrites")]
public async Task CheckStdoutWithLargeWrites(string path)
{
var deploymentParameters = Helpers.GetBaseDeploymentParameters("StartupExceptionWebsite");
deploymentParameters.EnvironmentVariables["ASPNETCORE_INPROCESS_STARTUP_VALUE"] = path;
var deploymentResult = await DeployAsync(deploymentParameters);
var response = await deploymentResult.RetryingHttpClient.GetAsync(path);
Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode);
Assert.Contains(TestSink.Writes, context => context.Message.Contains(new string('a', 4096)));
}
}
}

View File

@ -33,7 +33,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
var deploymentResult = await DeployAsync(deploymentParameters);
ModifyAspNetCoreSectionInWebConfig(deploymentResult, "processPath", "%DotnetPath%");
Helpers.ModifyAspNetCoreSectionInWebConfig(deploymentResult, "processPath", "%DotnetPath%");
var response = await deploymentResult.RetryingHttpClient.GetAsync("HelloWorld");
@ -52,7 +52,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
var deploymentResult = await DeployAsync(deploymentParameters);
ModifyAspNetCoreSectionInWebConfig(deploymentResult, "processPath", "%DotnetPath%");
Helpers.ModifyAspNetCoreSectionInWebConfig(deploymentResult, "processPath", "%DotnetPath%");
// Request to base address and check if various parts of the body are rendered & measure the cold startup time.
var response = await deploymentResult.RetryingHttpClient.GetAsync("HelloWorld");
@ -60,7 +60,6 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode);
}
public static TestMatrix TestVariants
=> TestMatrix.ForServers(ServerType.IISExpress)
.WithTfms(Tfm.NetCoreApp22)
@ -94,7 +93,8 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
Assert.Contains(TestSink.Writes, context => context.Message.Contains("Application is running inside IIS process but is not configured to use IIS server"));
}
private DeploymentParameters GetBaseDeploymentParameters(string site = "InProcessWebSite")
// Defaults to inprocess specific deployment parameters
public static DeploymentParameters GetBaseDeploymentParameters(string site = "InProcessWebSite")
{
return new DeploymentParameters(Helpers.GetTestWebSitePath(site), ServerType.IISExpress, RuntimeFlavor.CoreClr, RuntimeArchitecture.x64)
{
@ -105,16 +105,5 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
PublishApplicationBeforeDeployment = site == "InProcessWebSite",
};
}
private static void ModifyAspNetCoreSectionInWebConfig(IISDeploymentResult deploymentResult, string key, string value)
{
// modify the web.config after publish
var root = deploymentResult.DeploymentResult.ContentRoot;
var webConfigFile = $"{root}/web.config";
var config = XDocument.Load(webConfigFile);
var element = config.Descendants("aspNetCore").FirstOrDefault();
element.SetAttributeValue(key, value);
config.Save(webConfigFile);
}
}
}

View File

@ -2,6 +2,9 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.IO;
using System.Linq;
using System.Xml.Linq;
using Microsoft.AspNetCore.Server.IntegrationTesting;
using Microsoft.AspNetCore.Testing;
namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
@ -16,5 +19,32 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
public static string GetInProcessTestSitesPath() => GetTestWebSitePath("InProcessWebSite");
public static string GetOutOfProcessTestSitesPath() => GetTestWebSitePath("OutOfProcessWebSite");
public static void ModifyAspNetCoreSectionInWebConfig(IISDeploymentResult deploymentResult, string key, string value)
=> ModifySectionInWebConfig(deploymentResult, key, value, section: "aspNetCore", 0);
public static void ModifySectionInWebConfig(IISDeploymentResult deploymentResult, string key, string value, string section, int index)
{
// modify the web.config after publish
var root = deploymentResult.DeploymentResult.ContentRoot;
var webConfigFile = $"{root}/web.config";
var config = XDocument.Load(webConfigFile);
var element = config.Descendants(section).ToList()[index];
element.SetAttributeValue(key, value);
config.Save(webConfigFile);
}
// Defaults to inprocess specific deployment parameters
public static DeploymentParameters GetBaseDeploymentParameters(string site = "InProcessWebSite")
{
return new DeploymentParameters(Helpers.GetTestWebSitePath(site), ServerType.IISExpress, RuntimeFlavor.CoreClr, RuntimeArchitecture.x64)
{
TargetFramework = Tfm.NetCoreApp22,
ApplicationType = ApplicationType.Portable,
AncmVersion = AncmVersion.AspNetCoreModuleV2,
HostingModel = HostingModel.InProcess,
PublishApplicationBeforeDeployment = site == "InProcessWebSite",
};
}
}
}

View File

@ -657,7 +657,6 @@ namespace IISTestSite
{
app.Run(async context =>
{
if (context.Request.Path.StartsWithSegments("/NullBuffer"))
{
await context.Response.Body.WriteAsync(null, 0, 0);
@ -704,5 +703,27 @@ namespace IISTestSite
{
app.Run(async ctx => { await ctx.Response.WriteAsync(AppDomain.CurrentDomain.BaseDirectory); });
}
private void CheckLogFile(IApplicationBuilder app)
{
app.Run(async ctx =>
{
Console.WriteLine("TEST MESSAGE");
await ctx.Response.WriteAsync("Hello World");
});
}
private void CheckErrLogFile(IApplicationBuilder app)
{
app.Run(async ctx =>
{
Console.Error.WriteLine("TEST MESSAGE");
Console.Error.Flush();
await ctx.Response.WriteAsync("Hello World");
});
}
}
}

View File

@ -1,16 +1,16 @@
<?xml version="1.0"?>
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
<handlers>
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified" />
</handlers>
<aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false" hostingModel="inprocess">
<aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="true" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false" hostingModel="inprocess">
<environmentVariables>
<environmentVariable name="ASPNETCORE_INPROCESS_TESTING_VALUE" value="foobar" />
<environmentVariable name="ASPNETCORE_INPROCESS_TESTING_LONG_VALUE" value="AReallyLongValueThatIsGreaterThan300CharactersToForceResizeInNativeAReallyLongValueThatIsGreaterThan300CharactersToForceResizeInNativeAReallyLongValueThatIsGreaterThan300CharactersToForceResizeInNativeAReallyLongValueThatIsGreaterThan300CharactersToForceResizeInNativeAReallyLongValueThatIsGreaterThan300CharactersToForceResizeInNativeAReallyLongValueThatIsGreaterThan300CharactersToForceResizeInNative" />
<environmentVariable name="ProgramFiles" value="foobarbaz" />
<environmentVariable name="ASPNETCORE_IIS_HTTPAUTH" value="shouldberemoved"/>
<environmentVariable name="ASPNETCORE_IIS_HTTPAUTH" value="shouldberemoved" />
</environmentVariables>
</aspNetCore>
</system.webServer>
</configuration>
</configuration>

View File

@ -0,0 +1,46 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
namespace IISTestSite
{
public static class Program
{
public static void Main(string[] args)
{
var envVariable = Environment.GetEnvironmentVariable("ASPNETCORE_INPROCESS_STARTUP_VALUE");
var randomNumber = Environment.GetEnvironmentVariable("ASPNETCORE_INPROCESS_RANDOM_VALUE");
// Semicolons are appended to env variables; removing them.
if (envVariable == "CheckLargeStdOutWrites")
{
Console.WriteLine(new string('a', 4096));
}
else if (envVariable == "CheckLargeStdErrWrites")
{
Console.Error.WriteLine(new string('a', 4096));
Console.Error.Flush();
}
else if (envVariable == "CheckLogFile")
{
Console.WriteLine($"Random number: {randomNumber}");
}
else if (envVariable == "CheckErrLogFile")
{
Console.Error.WriteLine($"Random number: {randomNumber}");
Console.Error.Flush();
}
else if (envVariable == "CheckOversizedStdErrWrites")
{
Console.WriteLine(new string('a', 5000));
}
else if (envVariable == "CheckOversizedStdOutWrites")
{
Console.Error.WriteLine(new string('a', 4096));
Console.Error.Flush();
}
}
}
}

View File

@ -0,0 +1,37 @@
{
"iisSettings": {
"windowsAuthentication": true,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:5762/",
"sslPort": 0
}
},
"profiles": {
"ANCM IIS Express": {
"commandName": "Executable",
"executablePath": "$(IISExpressPath)",
"commandLineArgs": "$(IISExpressArguments)",
"nativeDebugging": true,
"environmentVariables": {
"IIS_SITE_PATH": "$(MSBuildThisFileDirectory)",
"ANCM_PATH": "$(TargetDir)$(AncmV2Path)",
"LAUNCHER_ARGS": "$(TargetPath)",
"ASPNETCORE_ENVIRONMENT": "Development",
"LAUNCHER_PATH": "$(DotNetPath)"
}
},
"ANCM IIS": {
"commandName": "Executable",
"executablePath": "$(IISPath)",
"commandLineArgs": "$(IISArguments)",
"environmentVariables": {
"IIS_SITE_PATH": "$(MSBuildThisFileDirectory)",
"ANCM_PATH": "$(TargetDir)$(AncmV2Path)",
"LAUNCHER_ARGS": "$(TargetPath)",
"ASPNETCORE_ENVIRONMENT": "Development",
"LAUNCHER_PATH": "$(DotNetPath)"
}
}
}
}

View File

@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<Import Project="..\..\..\build\testsite.props" />
<PropertyGroup>
<TargetFrameworks>netcoreapp2.2</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\Microsoft.AspNetCore.Server.IIS\Microsoft.AspNetCore.Server.IIS.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Hosting" Version="$(MicrosoftAspNetCoreHostingPackageVersion)" />
</ItemGroup>
</Project>