summaryrefslogtreecommitdiffhomepage
path: root/windows/nsis-plugins/src/log/logger.cpp
blob: 6ce7312744243d7c2ba6b9509fd5490304662965 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
#include "stdafx.h"
#include "logger.h"
#include <libcommon/error.h>
#include <libcommon/string.h>
#include <sstream>
#include <iomanip>

Utf8FileLogSink::Utf8FileLogSink(const std::wstring &file, bool append, bool flush)
	: m_flush(flush)
{
	const DWORD creationDisposition = (append ? OPEN_ALWAYS : CREATE_ALWAYS);

	m_logfile = CreateFileW(file.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, nullptr,
		creationDisposition, FILE_ATTRIBUTE_NORMAL, nullptr);

	if (INVALID_HANDLE_VALUE == m_logfile)
	{
		THROW_WINDOWS_ERROR(GetLastError(), "Open/create log file");
	}

	if (append && ERROR_ALREADY_EXISTS == GetLastError())
	{
		LARGE_INTEGER offset = { 0 };

		const auto seekStatus = SetFilePointerEx(m_logfile, offset, nullptr, FILE_END);

		if (FALSE == seekStatus)
		{
			CloseHandle(m_logfile);
			THROW_WINDOWS_ERROR(GetLastError(), "Seek to end offset in existing log file");
		}
	}
}

Utf8FileLogSink::~Utf8FileLogSink()
{
	CloseHandle(m_logfile);
}

void Utf8FileLogSink::log(const std::wstring &message)
{
	auto utf8String = common::string::ToUtf8(message);
	utf8String.pop_back(); // remove the null char

	if (0 == utf8String.size())
	{
		return;
	}

	utf8String.push_back('\xd');
	utf8String.push_back('\xa');

	DWORD bytesWritten;

	WriteFile(m_logfile, utf8String.data(), utf8String.size(), &bytesWritten, nullptr);

	if (m_flush)
	{
		FlushFileBuffers(m_logfile);
	}
}

void Logger::log(const std::wstring &message)
{
	m_logsink->log(Compose(message, Timestamp()));
}

void Logger::log(const std::wstring &message, const std::vector<std::wstring> &details)
{
	const auto timestamp = this->Timestamp();

	m_logsink->log(Compose(message, timestamp));

	//
	// Write details with indentation.
	//
	for (const auto detail : details)
	{
		m_logsink->log(Compose(detail, timestamp, 4));
	}
}

// static
std::wstring Logger::Timestamp()
{
	SYSTEMTIME time;

	GetLocalTime(&time);

	std::wstringstream ss;

	ss << L'['
		<< std::right << std::setw(4) << std::setfill(L'0') << time.wYear
		<< L'-'
		<< std::right << std::setw(2) << std::setfill(L'0') << time.wMonth
		<< L'-'
		<< std::right << std::setw(2) << std::setfill(L'0') << time.wDay
		<< L' '
		<< std::right << std::setw(2) << std::setfill(L'0') << time.wHour
		<< L':'
		<< std::right << std::setw(2) << std::setfill(L'0') << time.wMinute
		<< L':'
		<< std::right << std::setw(2) << std::setfill(L'0') << time.wSecond
		<< L'.'
		<< std::right << std::setw(3) << std::setfill(L'0') << time.wMilliseconds
		<< L']';

	return ss.str();
}

//static
std::wstring Logger::Compose(const std::wstring &message, const std::wstring &timestamp, size_t indentation)
{
	std::wstringstream ss;

	ss << timestamp << L' '
		<< std::wstring(indentation, L' ')
		<< message;

	return ss.str();
}