1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. |
2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) |
3 | |
4 | #pragma once |
5 | |
6 | #ifndef SPDLOG_HEADER_ONLY |
7 | # include <spdlog/sinks/stdout_sinks.h> |
8 | #endif |
9 | |
10 | #include <spdlog/details/console_globals.h> |
11 | #include <spdlog/pattern_formatter.h> |
12 | #include <memory> |
13 | |
14 | #ifdef _WIN32 |
15 | // under windows using fwrite to non-binary stream results in \r\r\n (see issue #1675) |
16 | // so instead we use ::FileWrite |
17 | # include <spdlog/details/windows_include.h> |
18 | |
19 | # ifndef _USING_V110_SDK71_ // fileapi.h doesn't exist in winxp |
20 | # include <fileapi.h> // WriteFile (..) |
21 | # endif |
22 | |
23 | # include <io.h> // _get_osfhandle(..) |
24 | # include <stdio.h> // _fileno(..) |
25 | #endif // WIN32 |
26 | |
27 | namespace spdlog { |
28 | |
29 | namespace sinks { |
30 | |
31 | template<typename ConsoleMutex> |
32 | SPDLOG_INLINE stdout_sink_base<ConsoleMutex>::stdout_sink_base(FILE *file) |
33 | : mutex_(ConsoleMutex::mutex()) |
34 | , file_(file) |
35 | , formatter_(details::make_unique<spdlog::pattern_formatter>()) |
36 | { |
37 | #ifdef _WIN32 |
38 | // get windows handle from the FILE* object |
39 | |
40 | handle_ = reinterpret_cast<HANDLE>(::_get_osfhandle(::_fileno(file_))); |
41 | |
42 | // don't throw to support cases where no console is attached, |
43 | // and let the log method to do nothing if (handle_ == INVALID_HANDLE_VALUE). |
44 | // throw only if non stdout/stderr target is requested (probably regular file and not console). |
45 | if (handle_ == INVALID_HANDLE_VALUE && file != stdout && file != stderr) |
46 | { |
47 | throw_spdlog_ex("spdlog::stdout_sink_base: _get_osfhandle() failed" , errno); |
48 | } |
49 | #endif // WIN32 |
50 | } |
51 | |
52 | template<typename ConsoleMutex> |
53 | SPDLOG_INLINE void stdout_sink_base<ConsoleMutex>::log(const details::log_msg &msg) |
54 | { |
55 | #ifdef _WIN32 |
56 | if (handle_ == INVALID_HANDLE_VALUE) |
57 | { |
58 | return; |
59 | } |
60 | std::lock_guard<mutex_t> lock(mutex_); |
61 | memory_buf_t formatted; |
62 | formatter_->format(msg, formatted); |
63 | ::fflush(file_); // flush in case there is something in this file_ already |
64 | auto size = static_cast<DWORD>(formatted.size()); |
65 | DWORD bytes_written = 0; |
66 | bool ok = ::WriteFile(handle_, formatted.data(), size, &bytes_written, nullptr) != 0; |
67 | if (!ok) |
68 | { |
69 | throw_spdlog_ex("stdout_sink_base: WriteFile() failed. GetLastError(): " + std::to_string(::GetLastError())); |
70 | } |
71 | #else |
72 | std::lock_guard<mutex_t> lock(mutex_); |
73 | memory_buf_t formatted; |
74 | formatter_->format(msg, formatted); |
75 | ::fwrite(formatted.data(), sizeof(char), formatted.size(), file_); |
76 | ::fflush(file_); // flush every line to terminal |
77 | #endif // WIN32 |
78 | } |
79 | |
80 | template<typename ConsoleMutex> |
81 | SPDLOG_INLINE void stdout_sink_base<ConsoleMutex>::flush() |
82 | { |
83 | std::lock_guard<mutex_t> lock(mutex_); |
84 | fflush(file_); |
85 | } |
86 | |
87 | template<typename ConsoleMutex> |
88 | SPDLOG_INLINE void stdout_sink_base<ConsoleMutex>::set_pattern(const std::string &pattern) |
89 | { |
90 | std::lock_guard<mutex_t> lock(mutex_); |
91 | formatter_ = std::unique_ptr<spdlog::formatter>(new pattern_formatter(pattern)); |
92 | } |
93 | |
94 | template<typename ConsoleMutex> |
95 | SPDLOG_INLINE void stdout_sink_base<ConsoleMutex>::set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) |
96 | { |
97 | std::lock_guard<mutex_t> lock(mutex_); |
98 | formatter_ = std::move(sink_formatter); |
99 | } |
100 | |
101 | // stdout sink |
102 | template<typename ConsoleMutex> |
103 | SPDLOG_INLINE stdout_sink<ConsoleMutex>::stdout_sink() |
104 | : stdout_sink_base<ConsoleMutex>(stdout) |
105 | {} |
106 | |
107 | // stderr sink |
108 | template<typename ConsoleMutex> |
109 | SPDLOG_INLINE stderr_sink<ConsoleMutex>::stderr_sink() |
110 | : stdout_sink_base<ConsoleMutex>(stderr) |
111 | {} |
112 | |
113 | } // namespace sinks |
114 | |
115 | // factory methods |
116 | template<typename Factory> |
117 | SPDLOG_INLINE std::shared_ptr<logger> stdout_logger_mt(const std::string &logger_name) |
118 | { |
119 | return Factory::template create<sinks::stdout_sink_mt>(logger_name); |
120 | } |
121 | |
122 | template<typename Factory> |
123 | SPDLOG_INLINE std::shared_ptr<logger> stdout_logger_st(const std::string &logger_name) |
124 | { |
125 | return Factory::template create<sinks::stdout_sink_st>(logger_name); |
126 | } |
127 | |
128 | template<typename Factory> |
129 | SPDLOG_INLINE std::shared_ptr<logger> stderr_logger_mt(const std::string &logger_name) |
130 | { |
131 | return Factory::template create<sinks::stderr_sink_mt>(logger_name); |
132 | } |
133 | |
134 | template<typename Factory> |
135 | SPDLOG_INLINE std::shared_ptr<logger> stderr_logger_st(const std::string &logger_name) |
136 | { |
137 | return Factory::template create<sinks::stderr_sink_st>(logger_name); |
138 | } |
139 | } // namespace spdlog |
140 | |