196 lines
5.6 KiB
C++
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;
|
|
}
|