Alamo
OutputLog.cpp
Go to the documentation of this file.
1#include "IO/OutputLog.H"
2
3#include <fstream>
4#include <iostream>
5#include <mutex>
6#include <stdexcept>
7#include <streambuf>
8#include <string>
9
10namespace
11{
12
13class LogSink
14{
15public:
16 void Write(const char* data, std::streamsize size)
17 {
18 std::lock_guard<std::mutex> lock(m_mutex);
19 if (m_state == State::Buffering)
20 m_pending.append(data, static_cast<std::size_t>(size));
21 else if (m_state == State::Open)
22 m_file.write(data, size);
23 }
24
25 int Sync()
26 {
27 std::lock_guard<std::mutex> lock(m_mutex);
28 if (m_state != State::Open) return 0;
29 m_file.flush();
30 return m_file.good() ? 0 : -1;
31 }
32
33 void Open(const std::string& path)
34 {
35 std::lock_guard<std::mutex> lock(m_mutex);
36 m_file.open(path, std::ios::out | std::ios::trunc);
37 if (!m_file.is_open())
38 throw std::runtime_error("Could not open output log " + path);
39
40 m_state = State::Open;
41 m_file.write(
42 m_pending.data(),
43 static_cast<std::streamsize>(m_pending.size()));
44 m_pending.clear();
45 m_file.flush();
46 }
47
48 void Disable()
49 {
50 std::lock_guard<std::mutex> lock(m_mutex);
51 m_pending.clear();
52 m_state = State::Disabled;
53 }
54
55 void Close()
56 {
57 std::lock_guard<std::mutex> lock(m_mutex);
58 if (m_file.is_open())
59 {
60 m_file.flush();
61 m_file.close();
62 }
63 m_pending.clear();
64 m_state = State::Disabled;
65 }
66
67private:
68 enum class State
69 {
70 Buffering,
71 Open,
72 Disabled
73 };
74
75 std::mutex m_mutex;
76 std::ofstream m_file;
77 std::string m_pending;
78 State m_state = State::Buffering;
79};
80
81class TeeBuffer
82 : public std::streambuf
83{
84public:
85 TeeBuffer(std::streambuf* terminal, LogSink& log)
86 : m_terminal(terminal), m_log(log)
87 {
88 }
89
90protected:
91 int_type overflow(int_type character) override
92 {
93 if (traits_type::eq_int_type(character, traits_type::eof()))
94 return traits_type::not_eof(character);
95
96 const char value = traits_type::to_char_type(character);
97 if (traits_type::eq_int_type(
98 m_terminal->sputc(value), traits_type::eof()))
99 return traits_type::eof();
100 m_log.Write(&value, 1);
101 return character;
102 }
103
104 std::streamsize xsputn(
105 const char* data, std::streamsize size) override
106 {
107 const std::streamsize written = m_terminal->sputn(data, size);
108 m_log.Write(data, written);
109 return written;
110 }
111
112 int sync() override
113 {
114 const int terminal_status = m_terminal->pubsync();
115 const int log_status = m_log.Sync();
116 return terminal_status == 0 && log_status == 0 ? 0 : -1;
117 }
118
119private:
120 std::streambuf* m_terminal;
121 LogSink& m_log;
122};
123
124LogSink log_sink;
125std::streambuf* terminal_stdout = std::cout.rdbuf();
126std::streambuf* terminal_stderr = std::cerr.rdbuf();
127std::streambuf* terminal_stdlog = std::clog.rdbuf();
128TeeBuffer stdout_buffer(terminal_stdout, log_sink);
129TeeBuffer stderr_buffer(terminal_stderr, log_sink);
130TeeBuffer stdlog_buffer(terminal_stdlog, log_sink);
131bool initialized = false;
132
133}
134
136{
137
139{
140 if (initialized) return;
141 std::cout.rdbuf(&stdout_buffer);
142 std::cerr.rdbuf(&stderr_buffer);
143 std::clog.rdbuf(&stdlog_buffer);
144 initialized = true;
145}
146
147void Open(const std::string& path)
148{
149 log_sink.Open(path);
150}
151
153{
154 log_sink.Disable();
155}
156
158{
159 if (!initialized) return;
160
161 std::cout.flush();
162 std::cerr.flush();
163 std::clog.flush();
164 std::cout.rdbuf(terminal_stdout);
165 std::cerr.rdbuf(terminal_stderr);
166 std::clog.rdbuf(terminal_stdlog);
167 log_sink.Close();
168 initialized = false;
169}
170
171}
void Initialize()
Route stdout and stderr through the terminal/file tee.
void Finalize()
Flush the log and restore the original terminal stream buffers.
void DisableFile()
Stop retaining output on ranks that do not own the log file.
void Open(const std::string &path)
Open the rank-zero log after the output directory has been created.
bool initialized
Definition Util.cpp:119