Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
S
spdlog
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Libraries
spdlog
Commits
db1a2214
Commit
db1a2214
authored
Feb 04, 2020
by
bandana2004
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add eventlog_sink for logging to Windows Event Log (local only).
parent
966d827d
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
413 additions
and
0 deletions
+413
-0
include/spdlog/sinks/eventlog_sink.h
include/spdlog/sinks/eventlog_sink.h
+100
-0
include/spdlog/sinks/eventlog_sink_win32.h
include/spdlog/sinks/eventlog_sink_win32.h
+246
-0
tests/CMakeLists.txt
tests/CMakeLists.txt
+1
-0
tests/test_eventlog.cpp
tests/test_eventlog.cpp
+66
-0
No files found.
include/spdlog/sinks/eventlog_sink.h
0 → 100644
View file @
db1a2214
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
// Requires the following registry entries to be present, with the following modifications:
// 1. {app_name} should be replaced with your application name
// 2. {log_name} should be replaced with the specific log name and the key should be duplicated for
// each log used in the application
/*---------------------------------------------------------------------------------------
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\EventLog\{app_name}]
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\EventLog\{app_name}\{log_name}]
"TypesSupported"=dword:00000007
"EventMessageFile"=hex(2):25,00,73,00,79,00,73,00,74,00,65,00,6d,00,72,00,6f,\
00,6f,00,74,00,25,00,5c,00,53,00,79,00,73,00,74,00,65,00,6d,00,33,00,32,00,\
5c,00,6d,00,73,00,63,00,6f,00,72,00,65,00,65,00,2e,00,64,00,6c,00,6c,00,00,\
00
-----------------------------------------------------------------------------------------*/
#pragma once
#ifdef _WIN32
#include <winbase.h>
#endif
#include <spdlog/details/null_mutex.h>
#include <spdlog/sinks/base_sink.h>
#include <mutex>
#include <string>
namespace
spdlog
{
namespace
sinks
{
/*
* Windows Event Log sink
*/
template
<
typename
Mutex
>
class
eventlog_sink
:
public
base_sink
<
Mutex
>
{
public:
explicit
eventlog_sink
(
std
::
string
const
&
source
,
std
::
string
const
&
log
=
"Application"
,
std
::
string
const
&
message_file_path
=
"%windir%
\\
System32
\\
mscoree.dll"
);
eventlog_sink
(
eventlog_sink
const
&
)
=
delete
;
eventlog_sink
&
operator
=
(
eventlog_sink
const
&
)
=
delete
;
protected:
void
sink_it_
(
const
details
::
log_msg
&
msg
)
override
;
void
flush_
()
override
{}
virtual
void
set_pattern_
(
const
std
::
string
&
pattern
)
override
;
virtual
void
set_formatter_
(
std
::
unique_ptr
<
spdlog
::
formatter
>
sink_formatter
)
override
;
private:
std
::
unique_ptr
<
sink
>
impl_
;
};
#ifdef _WIN32
#include "eventlog_sink_win32.h"
#else
template
<
typename
Mutex
>
eventlog_sink
<
Mutex
>::
eventlog_sink
(
std
::
string
const
&
source
,
std
::
string
const
&
log
,
std
::
string
const
&
message_file_path
)
{
}
template
<
typename
Mutex
>
void
eventlog_sink
<
Mutex
>::
sink_it_
(
const
details
::
log_msg
&
msg
)
{}
#endif
template
<
typename
Mutex
>
void
eventlog_sink
<
Mutex
>::
set_pattern_
(
const
std
::
string
&
pattern
)
{
if
(
impl_
)
impl_
->
set_pattern
(
pattern
);
}
template
<
typename
Mutex
>
void
eventlog_sink
<
Mutex
>::
set_formatter_
(
std
::
unique_ptr
<
spdlog
::
formatter
>
sink_formatter
)
{
if
(
impl_
)
impl_
->
set_formatter
(
std
::
move
(
sink_formatter
));
}
using
eventlog_sink_mt
=
eventlog_sink
<
std
::
mutex
>
;
using
eventlog_sink_st
=
eventlog_sink
<
details
::
null_mutex
>
;
using
windebug_sink_mt
=
eventlog_sink_mt
;
using
windebug_sink_st
=
eventlog_sink_st
;
}
// namespace sinks
}
// namespace spdlog
include/spdlog/sinks/eventlog_sink_win32.h
0 → 100644
View file @
db1a2214
// Copyright(c) 2020 bandana2004@gmail.com
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
namespace
eventlog_sink_win32
{
struct
win32_error
:
public
spdlog_ex
{
static
std
::
string
format
(
std
::
string
const
&
func
,
DWORD
error
=
GetLastError
())
{
return
fmt
::
format
(
func
+
" failed ({0})"
,
error
);
}
static
void
report
(
char
const
*
message
)
SPDLOG_NOEXCEPT
{
fprintf
(
stderr
,
"%s"
,
message
);
fflush
(
stderr
);
}
static
void
report
(
std
::
string
const
&
message
)
SPDLOG_NOEXCEPT
{
report
(
message
.
c_str
());
}
win32_error
(
std
::
string
const
&
func_name
,
DWORD
error
=
GetLastError
())
:
spdlog_ex
(
format
(
func_name
,
error
),
error
)
{}
};
struct
sid
{
static
SID
*
duplicate
(
SID
*
p_src_sid
)
{
if
(
!
IsValidSid
(
p_src_sid
))
SPDLOG_THROW
(
spdlog_ex
(
"sid::duplicate: invalid SID received"
));
DWORD
sid_size
=
::
GetLengthSid
(
p_src_sid
);
SID
*
p_dest_sid
=
(
SID
*
)
::
HeapAlloc
(
GetProcessHeap
(),
HEAP_ZERO_MEMORY
,
sid_size
);
if
(
!
p_dest_sid
)
SPDLOG_THROW
(
win32_error
(
"HeapAlloc"
));
if
(
!
CopySid
(
sid_size
,
p_dest_sid
,
p_src_sid
))
{
sid
::
free
(
&
p_dest_sid
);
SPDLOG_THROW
(
win32_error
(
"CopySid"
));
}
return
p_dest_sid
;
}
static
void
free
(
SID
**
ppsid
)
{
if
(
ppsid
&&
*
ppsid
)
{
if
(
!
HeapFree
(
GetProcessHeap
(),
0
,
(
LPVOID
)
*
ppsid
))
{
SPDLOG_THROW
(
win32_error
(
"HeapFree"
));
}
}
*
ppsid
=
nullptr
;
}
static
SID
*
get_current_user_sid
()
{
struct
process_token_t
{
HANDLE
hToken_
;
bool
hasToken_
;
process_token_t
(
HANDLE
process
)
:
hToken_
(
0
)
,
hasToken_
(
OpenProcessToken
(
process
,
TOKEN_QUERY
,
&
hToken_
))
{
if
(
!
hasToken_
)
SPDLOG_THROW
(
win32_error
(
"OpenProcessToken"
));
}
~
process_token_t
()
{
if
(
hasToken_
&&
!
CloseHandle
(
hToken_
))
win32_error
::
report
(
win32_error
::
format
(
"CloseHandle"
));
}
}
current_process_token
(
GetCurrentProcess
());
// GetCurrentProcess returns pseudohandle, no leak here!
// Get the required size
DWORD
tusize
=
0
;
GetTokenInformation
(
current_process_token
.
hToken_
,
TokenUser
,
NULL
,
0
,
&
tusize
);
if
(
GetLastError
()
!=
ERROR_INSUFFICIENT_BUFFER
)
SPDLOG_THROW
(
win32_error
(
"GetTokenInformation"
));
std
::
vector
<
unsigned
char
>
buffer
(
tusize
);
if
(
!
GetTokenInformation
(
current_process_token
.
hToken_
,
TokenUser
,
(
LPVOID
)
buffer
.
data
(),
tusize
,
&
tusize
))
SPDLOG_THROW
(
win32_error
(
"GetTokenInformation"
));
return
sid
::
duplicate
((
SID
*
)
((
TOKEN_USER
*
)
buffer
.
data
())
->
User
.
Sid
);
}
};
struct
eventlog
{
static
WORD
get_event_type
(
details
::
log_msg
const
&
msg
)
{
WORD
type
=
EVENTLOG_SUCCESS
;
if
(
msg
.
level
>=
level
::
info
)
{
type
=
EVENTLOG_INFORMATION_TYPE
;
if
(
msg
.
level
>=
level
::
warn
)
{
type
=
EVENTLOG_WARNING_TYPE
;
if
(
msg
.
level
>=
level
::
err
)
{
type
=
EVENTLOG_ERROR_TYPE
;
}
}
}
return
type
;
}
static
WORD
get_event_category
(
details
::
log_msg
const
&
msg
)
{
WORD
category
=
1
;
if
(
msg
.
level
>=
level
::
debug
)
{
category
=
2
;
if
(
msg
.
level
>=
level
::
info
)
{
category
=
3
;
if
(
msg
.
level
>=
level
::
warn
)
{
category
=
4
;
if
(
msg
.
level
>=
level
::
err
)
{
category
=
5
;
if
(
msg
.
level
>=
level
::
critical
)
{
category
=
6
;
}
}
}
}
}
return
category
;
}
};
class
sink
:
public
base_sink
<
spdlog
::
details
::
null_mutex
>
{
private:
HANDLE
hEventLog_
;
SID
*
current_user_sid_
;
std
::
string
source_
;
std
::
string
log_
;
void
add_registry_info
(
std
::
string
const
&
message_file_path
)
{
std
::
string
subkey
(
"SYSTEM
\\
CurrentControlSet
\\
Services
\\
EventLog
\\
"
);
subkey
+=
log_
+
"
\\
"
+
source_
;
::
HKEY
hkey
{};
DWORD
disposition
{};
long
stat
=
RegCreateKeyEx
(
HKEY_LOCAL_MACHINE
,
subkey
.
c_str
(),
0
,
NULL
,
REG_OPTION_NON_VOLATILE
,
KEY_SET_VALUE
,
NULL
,
&
hkey
,
&
disposition
);
if
(
stat
==
ERROR_SUCCESS
)
{
if
(
disposition
==
REG_CREATED_NEW_KEY
&&
!
message_file_path
.
empty
())
{
auto
const
expanded_message_file_path_length
=
ExpandEnvironmentStrings
(
message_file_path
.
c_str
(),
(
LPSTR
)
&
disposition
,
0
);
std
::
vector
<
char
>
expanded_message_file_path
(
expanded_message_file_path_length
+
1
);
ExpandEnvironmentStrings
(
message_file_path
.
c_str
(),
expanded_message_file_path
.
data
(),
expanded_message_file_path_length
);
RegSetValueEx
(
hkey
,
"EventMessageFile"
,
0
,
REG_SZ
,
(
LPBYTE
)
expanded_message_file_path
.
data
(),
expanded_message_file_path_length
);
DWORD
typesSupported
=
7
;
RegSetValueEx
(
hkey
,
"TypesSupported"
,
0
,
REG_DWORD
,
(
LPBYTE
)
&
typesSupported
,
sizeof
(
DWORD
));
}
}
else
{
SPDLOG_THROW
(
win32_error
(
"RegCreateKeyEx"
,
stat
));
}
RegCloseKey
(
hkey
);
}
protected:
void
sink_it_
(
const
details
::
log_msg
&
msg
)
override
{
memory_buf_t
formatted
;
formatter_
->
format
(
msg
,
formatted
);
auto
formatted_string
=
fmt
::
to_string
(
formatted
);
auto
formatted_string_lpsz
=
formatted_string
.
c_str
();
if
(
!
ReportEvent
(
hEventLog_
,
eventlog
::
get_event_type
(
msg
),
eventlog
::
get_event_category
(
msg
),
1000
,
current_user_sid_
,
1
,
0
,
&
formatted_string_lpsz
,
nullptr
))
SPDLOG_THROW
(
win32_error
(
"ReportEvent"
));
}
void
flush_
()
override
{}
public:
sink
(
std
::
string
const
&
source
,
std
::
string
const
&
log
,
std
::
string
const
&
message_file_path
)
:
source_
(
source
)
,
log_
(
log
)
{
if
(
!
message_file_path
.
empty
())
add_registry_info
(
message_file_path
);
current_user_sid_
=
sid
::
get_current_user_sid
();
hEventLog_
=
RegisterEventSource
(
nullptr
,
source
.
c_str
());
if
(
!
hEventLog_
)
SPDLOG_THROW
(
win32_error
(
"RegisterEventSource"
));
}
~
sink
()
{
try
{
if
(
hEventLog_
&&
!
DeregisterEventSource
(
hEventLog_
))
win32_error
::
report
(
win32_error
::
format
(
"DeregisterEventSource"
));
sid
::
free
(
&
current_user_sid_
);
}
catch
(
std
::
exception
const
&
e
)
{
win32_error
::
report
(
e
.
what
());
}
}
};
}
// namespace eventlog_sink_win32
template
<
typename
Mutex
>
eventlog_sink
<
Mutex
>::
eventlog_sink
(
std
::
string
const
&
source
,
std
::
string
const
&
log
,
std
::
string
const
&
message_file_path
)
:
impl_
(
std
::
make_unique
<
eventlog_sink_win32
::
sink
>
(
source
,
log
,
message_file_path
))
{}
template
<
typename
Mutex
>
void
eventlog_sink
<
Mutex
>::
sink_it_
(
const
details
::
log_msg
&
msg
)
{
impl_
->
log
(
msg
);
}
tests/CMakeLists.txt
View file @
db1a2214
...
...
@@ -12,6 +12,7 @@ set(SPDLOG_UTESTS_SOURCES
test_file_logging.cpp
test_daily_logger.cpp
test_misc.cpp
test_eventlog.cpp
test_pattern_formatter.cpp
test_async.cpp
test_registry.cpp
...
...
tests/test_eventlog.cpp
0 → 100644
View file @
db1a2214
#include "includes.h"
#include "test_sink.h"
#include "spdlog/fmt/bin_to_hex.h"
#include "spdlog/sinks/eventlog_sink.h"
#if _WIN32
static
void
test_single_print
(
std
::
function
<
void
(
std
::
string
const
&
)
>
do_print
,
std
::
string
const
&
expectedContents
,
WORD
expectedEventType
)
{
do_print
(
expectedContents
);
using
namespace
std
::
chrono
;
const
auto
expectedTimeGenerated
=
duration_cast
<
seconds
>
(
system_clock
::
now
().
time_since_epoch
()).
count
();
struct
handle_t
{
HANDLE
handle_
;
~
handle_t
()
{
if
(
handle_
)
REQUIRE
(
CloseEventLog
(
handle_
));
}
}
eventLog
{
OpenEventLog
(
nullptr
,
"my_source"
)};
REQUIRE
(
eventLog
.
handle_
);
DWORD
readBytes
{},
sizeNeeded
{};
auto
ok
=
ReadEventLog
(
eventLog
.
handle_
,
EVENTLOG_SEQUENTIAL_READ
|
EVENTLOG_BACKWARDS_READ
,
0
,
&
readBytes
,
0
,
&
readBytes
,
&
sizeNeeded
);
REQUIRE
(
!
ok
);
REQUIRE
(
GetLastError
()
==
ERROR_INSUFFICIENT_BUFFER
);
std
::
vector
<
char
>
recordBuffer
(
sizeNeeded
);
PEVENTLOGRECORD
record
=
(
PEVENTLOGRECORD
)
recordBuffer
.
data
();
ok
=
ReadEventLog
(
eventLog
.
handle_
,
EVENTLOG_SEQUENTIAL_READ
|
EVENTLOG_BACKWARDS_READ
,
0
,
record
,
sizeNeeded
,
&
readBytes
,
&
sizeNeeded
);
REQUIRE
(
ok
);
REQUIRE
(
record
->
NumStrings
==
1
);
REQUIRE
(
record
->
EventType
==
expectedEventType
);
REQUIRE
(
record
->
TimeGenerated
==
expectedTimeGenerated
);
std
::
string
message_in_log
(((
char
*
)
record
+
record
->
StringOffset
));
REQUIRE
(
message_in_log
==
expectedContents
+
"
\r\n
"
);
}
TEST_CASE
(
"eventlog"
,
"[eventlog]"
)
{
using
namespace
spdlog
;
auto
test_sink
=
std
::
make_shared
<
sinks
::
eventlog_sink_mt
>
(
"my_source"
,
"my_spd_log"
);
spdlog
::
logger
test_logger
(
"eventlog"
,
test_sink
);
test_logger
.
set_level
(
level
::
trace
);
test_sink
->
set_pattern
(
"%v"
);
test_single_print
([
&
test_logger
]
(
std
::
string
const
&
msg
)
{
test_logger
.
debug
(
msg
);
},
"my debug message"
,
EVENTLOG_SUCCESS
);
test_single_print
([
&
test_logger
]
(
std
::
string
const
&
msg
)
{
test_logger
.
info
(
msg
);
},
"my info message"
,
EVENTLOG_INFORMATION_TYPE
);
test_single_print
([
&
test_logger
]
(
std
::
string
const
&
msg
)
{
test_logger
.
warn
(
msg
);
},
"my warn message"
,
EVENTLOG_WARNING_TYPE
);
test_single_print
([
&
test_logger
]
(
std
::
string
const
&
msg
)
{
test_logger
.
error
(
msg
);
},
"my error message"
,
EVENTLOG_ERROR_TYPE
);
test_single_print
([
&
test_logger
]
(
std
::
string
const
&
msg
)
{
test_logger
.
critical
(
msg
);
},
"my critical message"
,
EVENTLOG_ERROR_TYPE
);
}
#endif
\ No newline at end of file
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment