aspnetcore/src/AspNetCoreModuleV2/RequestHandlerLib/FileOutputManager.cpp

196 lines
5.6 KiB
C++

// 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 "FileOutputManager.h"
#include "sttimer.h"
#include "exceptions.h"
#include "debugutil.h"
#include "SRWExclusiveLock.h"
#include "file_utility.h"
FileOutputManager::FileOutputManager() :
m_hLogFileHandle(INVALID_HANDLE_VALUE),
m_fdPreviousStdOut(-1),
m_fdPreviousStdErr(-1),
m_disposed(false)
{
InitializeSRWLock(&m_srwLock);
}
FileOutputManager::~FileOutputManager()
{
Stop();
}
HRESULT
FileOutputManager::Initialize(PCWSTR pwzStdOutLogFileName, PCWSTR pwzApplicationPath)
{
RETURN_IF_FAILED(m_wsApplicationPath.Copy(pwzApplicationPath));
RETURN_IF_FAILED(m_wsStdOutLogFileName.Copy(pwzStdOutLogFileName));
return S_OK;
}
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()
{
SYSTEMTIME systemTime;
SECURITY_ATTRIBUTES saAttr = { 0 };
STRU struPath;
RETURN_IF_FAILED(FILE_UTILITY::ConvertPathToFullPath(
m_wsStdOutLogFileName.QueryStr(),
m_wsApplicationPath.QueryStr(),
&struPath));
RETURN_IF_FAILED(FILE_UTILITY::EnsureDirectoryPathExist(struPath.QueryStr()));
GetSystemTime(&systemTime);
RETURN_IF_FAILED(
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()));
m_fdPreviousStdOut = _dup(_fileno(stdout));
m_fdPreviousStdErr = _dup(_fileno(stderr));
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)
{
return LOG_IF_FAILED(HRESULT_FROM_WIN32(GetLastError()));
}
// There are a few options for redirecting stdout/stderr,
// but there are issues with most of them.
// AllocConsole()
// *stdout = *m_pStdFile;
// *stderr = *m_pStdFile;
// Calling _dup2 on stderr fails on IIS. IIS sets stderr to -2
// _dup2(_fileno(m_pStdFile), _fileno(stdout));
// _dup2(_fileno(m_pStdFile), _fileno(stderr));
// If we were okay setting stdout and stderr to separate files, we could use:
// _wfreopen_s(&m_pStdFile, struLogFileName.QueryStr(), L"w+", stdout);
// _wfreopen_s(&m_pStdFile, struLogFileName.QueryStr(), L"w+", stderr);
// Calling SetStdHandle works for redirecting managed logs, however you cannot
// capture native logs (including hostfxr failures).
RETURN_LAST_ERROR_IF(!SetStdHandle(STD_OUTPUT_HANDLE, m_hLogFileHandle));
RETURN_LAST_ERROR_IF(!SetStdHandle(STD_ERROR_HANDLE, m_hLogFileHandle));
// Periodically flush the log content to file
m_Timer.InitializeTimer(STTIMER::TimerCallback, &m_struLogFilePath, 3000, 3000);
LOG_INFOF("Created log file for inprocess application: %S",
m_struLogFilePath.QueryStr());
return S_OK;
}
HRESULT
FileOutputManager::Stop()
{
if (m_disposed)
{
return S_OK;
}
SRWExclusiveLock lock(m_srwLock);
if (m_disposed)
{
return S_OK;
}
m_disposed = true;
HANDLE handle = NULL;
WIN32_FIND_DATA fileData;
if (m_hLogFileHandle != INVALID_HANDLE_VALUE)
{
m_Timer.CancelTimer();
}
// delete empty log file
handle = FindFirstFile(m_struLogFilePath.QueryStr(), &fileData);
if (handle != INVALID_HANDLE_VALUE &&
handle != NULL &&
fileData.nFileSizeHigh == 0 &&
fileData.nFileSizeLow == 0) // skip check of nFileSizeHigh
{
FindClose(handle);
LOG_LAST_ERROR_IF(!DeleteFile(m_struLogFilePath.QueryStr()));
}
if (m_fdPreviousStdOut >= 0)
{
LOG_LAST_ERROR_IF(!SetStdHandle(STD_OUTPUT_HANDLE, (HANDLE)_get_osfhandle(m_fdPreviousStdOut)));
LOG_INFOF("Restoring original stdout: %d", m_fdPreviousStdOut);
}
if (m_fdPreviousStdErr >= 0)
{
LOG_LAST_ERROR_IF(!SetStdHandle(STD_ERROR_HANDLE, (HANDLE)_get_osfhandle(m_fdPreviousStdErr)));
LOG_INFOF("Restoring original stderr: %d", m_fdPreviousStdOut);
}
return S_OK;
}