deal.II version GIT relicensing-1941-ga9013bea74 2024-10-08 06:50:00+00:00
\(\newcommand{\dealvcentcolon}{\mathrel{\mathop{:}}}\) \(\newcommand{\dealcoloneq}{\dealvcentcolon\mathrel{\mkern-1.2mu}=}\) \(\newcommand{\jump}[1]{\left[\!\left[ #1 \right]\!\right]}\) \(\newcommand{\average}[1]{\left\{\!\left\{ #1 \right\}\!\right\}}\)
Loading...
Searching...
No Matches
logstream.cc
Go to the documentation of this file.
1// ------------------------------------------------------------------------
2//
3// SPDX-License-Identifier: LGPL-2.1-or-later
4// Copyright (C) 1998 - 2024 by the deal.II authors
5//
6// This file is part of the deal.II library.
7//
8// Part of the source code is dual licensed under Apache-2.0 WITH
9// LLVM-exception OR LGPL-2.1-or-later. Detailed license information
10// governing the source code and code contributions can be found in
11// LICENSE.md and CONTRIBUTING.md at the top level directory of deal.II.
12//
13// ------------------------------------------------------------------------
14
18
19#include <fstream>
20#include <iomanip>
21#include <iostream>
22#include <sstream>
23#include <thread>
24
25
27
28namespace
29{
30 std::mutex log_lock;
31 std::mutex write_lock;
32} // namespace
33
34
35// The standard log object of deal.II:
37
38
39
40LogStream::Prefix::Prefix(const std::string &text)
41 : stream(&deallog)
42{
43 stream->push(text);
44}
45
46
47
48LogStream::Prefix::Prefix(const std::string &text, LogStream &s)
49 : stream(&s)
50{
51 stream->push(text);
52}
53
54
55
57{
58 // destructors may not throw exceptions. if the pop() function
59 // actually does throw one, then ignore it
60 try
61 {
62 stream->pop();
63 }
64 catch (...)
65 {
66 AssertNothrow(false,
68 "An exception occurred in LogStream::Prefix::~Prefix."));
69 }
70}
71
72
73
75 : parent_thread(std::this_thread::get_id())
76 , std_out(&std::cout)
77 , file(nullptr)
78 , std_depth(0)
79 , file_depth(10000)
80 , print_thread_id(false)
81 , at_newline(true)
82{
83 get_prefixes().emplace("DEAL:");
84}
85
86
87
89{
90 // if there was anything left in the stream that is current to this
91 // thread, make sure we flush it before it gets lost
92 {
93 if (get_stream().str().size() > 0)
94 {
95 // except the situation is not quite that simple. if this object is
96 // the 'deallog' object, then it is destroyed upon exit of the
97 // program. since it's defined in a shared library that depends on
98 // libstdc++.so, destruction happens before destruction of
99 // std::cout/cerr, but after all file variables defined in user
100 // programs have been destroyed. in other words, if we get here and
101 // the object being destroyed is 'deallog' and if 'deallog' is
102 // associated with a file stream, then we're in trouble: we'll try
103 // to write to a file that doesn't exist any more, and we're likely
104 // going to crash (this is tested by base/log_crash_01). rather
105 // than letting it come to this, print a message to the screen
106 // (note that we can't issue an assertion here either since Assert
107 // may want to write to 'deallog' itself, and AssertThrow will
108 // throw an exception that can't be caught)
109 if ((this == &deallog) && (file != nullptr))
110 *std_out << ("You still have content that was written to 'deallog' "
111 "but not flushed to the screen or a file while the "
112 "program is being terminated. This would lead to a "
113 "segmentation fault. Make sure you flush the "
114 "content of the 'deallog' object using 'std::endl' "
115 "before the end of the program.")
116 << std::endl;
117 else
118 *this << std::endl;
119 }
120 }
121}
122
123
125LogStream::operator<<(std::ostream &(*p)(std::ostream &))
126{
127 std::ostringstream &stream = get_stream();
128
129 // Print to the internal stringstream:
130 stream << p;
131
132
133 // This is a bloody hack until LogStream got reimplemented as a proper
134 // child of std::streambuf (or similar).
135 //
136 // The problem is that at this point we would like to know whether an
137 // std::flush or std::endl has called us, however, there is no way to
138 // detect this in a sane manner.
139 //
140 // The obvious idea to compare function pointers,
141 // std::ostream & (* const p_flush) (std::ostream &) = &std::flush;
142 // p == p_flush ? ...,
143 // is wrong as there doesn't has to be a _single_ std::flush instance...
144 // there could be multiple of it. And in fact, LLVM's libc++ implements
145 // std::flush and std::endl in a way that every shared library and
146 // executable has its local copy... fun...
147 //
148 // - Maier, 2013
149
150 class QueryStreambuf : public std::streambuf
151 {
152 // Implement a minimalistic stream buffer that only stores the fact
153 // whether overflow or sync was called
154 public:
155 QueryStreambuf()
156 : flushed_(false)
157 , newline_written_(false)
158 {}
159 bool
160 flushed()
161 {
162 return flushed_;
163 }
164 bool
165 newline_written()
166 {
167 return newline_written_;
168 }
169
170 private:
171 int_type
172 overflow(int_type ch) override
173 {
174 newline_written_ = true;
175 return ch;
176 }
177 int
178 sync() override
179 {
180 flushed_ = true;
181 return 0;
182 }
183 bool flushed_;
184 bool newline_written_;
185 } query_streambuf;
186
187 {
188 // and initialize an ostream with this streambuf:
189 std::ostream inject(&query_streambuf);
190 inject << p;
191 }
192
193 if (query_streambuf.flushed())
194 {
195 std::lock_guard<std::mutex> lock(write_lock);
196
197 // Print the line head in case of a previous newline:
198 if (at_newline)
200
201 at_newline = query_streambuf.newline_written();
202
203 if (get_prefixes().size() <= std_depth)
204 *std_out << stream.str();
205
206 if ((file != nullptr) && (get_prefixes().size() <= file_depth))
207 *file << stream.str() << std::flush;
208
209 // Start a new string:
210 stream.str("");
211 }
212
213 return *this;
214}
215
216
217void
218LogStream::attach(std::ostream &o,
219 const bool print_job_id,
220 const std::ios_base::fmtflags flags)
221{
222 std::lock_guard<std::mutex> lock(log_lock);
223 file = &o;
224 o.setf(flags);
225 if (print_job_id)
227}
228
229
230void
232{
233 std::lock_guard<std::mutex> lock(log_lock);
234 file = nullptr;
235}
236
237
238std::ostream &
240{
241 return *std_out;
242}
243
244
245
246std::ostringstream &
248{
249 // see if we have already created this stream. if not, do so and
250 // set the default flags (why we set these flags is lost to
251 // history, but this is what we need to keep several hundred tests
252 // from producing different output)
253 //
254 // note that in all of this we need not worry about thread-safety
255 // because we operate on a thread-local object and by definition
256 // there can only be one access at a time
257 if (outstreams.get().get() == nullptr)
258 {
259 outstreams.get() = std::make_shared<std::ostringstream>();
260 outstreams.get()->setf(std::ios::showpoint | std::ios::left);
261 }
262
263 // then return the stream
264 return *outstreams.get();
265}
266
267
268
269std::ostream &
271{
272 Assert(file,
273 ExcMessage("You can't ask for the std::ostream object for the output "
274 "file if none had been set before."));
275 return *file;
276}
277
278
279
280bool
282{
283 return (file != nullptr);
284}
285
286
287
288const std::string &
290{
291 static std::string empty_string;
292
293 if (get_prefixes().size() > 0)
294 return get_prefixes().top();
295 else
296 return empty_string;
297}
298
299
300
301void
302LogStream::push(const std::string &text)
303{
304 std::string pre;
305 if (get_prefixes().size() > 0)
306 pre = get_prefixes().top();
307
308 pre += text;
309 pre += std::string(":");
310 get_prefixes().push(pre);
311}
312
313
314
315void
317{
318 if (get_prefixes().size() > 0)
319 get_prefixes().pop();
320}
321
322
323
324std::ios::fmtflags
325LogStream::flags(const std::ios::fmtflags f)
326{
327 return get_stream().flags(f);
328}
329
330
331
332std::streamsize
333LogStream::precision(const std::streamsize prec)
334{
335 return get_stream().precision(prec);
336}
337
338
339
340std::streamsize
341LogStream::width(const std::streamsize wide)
342{
343 return get_stream().width(wide);
344}
345
346
347
348unsigned int
349LogStream::depth_console(const unsigned int n)
350{
351 std::lock_guard<std::mutex> lock(log_lock);
352 const unsigned int h = std_depth;
353 std_depth = n;
354 return h;
355}
356
357
358
359unsigned int
360LogStream::depth_file(const unsigned int n)
361{
362 std::lock_guard<std::mutex> lock(log_lock);
363 const unsigned int h = file_depth;
364 file_depth = n;
365 return h;
366}
367
368
369
370bool
372{
373 std::lock_guard<std::mutex> lock(log_lock);
374 const bool h = print_thread_id;
375 print_thread_id = flag;
376 return h;
377}
378
379
380
381std::stack<std::string> &
383{
384 bool exists = false;
385 std::stack<std::string> &local_prefixes = prefixes.get(exists);
386
387 // If this is a new locally stored stack, copy the "blessed" prefixes
388 // from the initial thread that created logstream.
389 if (exists == false)
390 {
391 const auto parent_prefixes = prefixes.get_for_thread(parent_thread);
392 if (parent_prefixes)
393 local_prefixes = parent_prefixes.value();
394 }
395
396 return local_prefixes;
397}
398
399
400
401void
403{
404 const std::string &head = get_prefix();
405 const std::thread::id thread = std::this_thread::get_id();
406
407 if (get_prefixes().size() <= std_depth)
408 {
409 if (print_thread_id)
410 *std_out << '[' << thread << ']';
411
412 if (head.size() > 0)
413 *std_out << head << ':';
414 }
415
416 if ((file != nullptr) && (get_prefixes().size() <= file_depth))
417 {
418 if (print_thread_id)
419 *file << '[' << thread << ']';
420
421 if (head.size() > 0)
422 *file << head << ':';
423 }
424}
425
static const JobIdentifier & get_dealjobid()
ObserverPointer< LogStream, LogStream::Prefix > stream
Definition logstream.h:125
Prefix(const std::string &text)
Definition logstream.cc:40
std::thread::id parent_thread
Definition logstream.h:328
std::streamsize width(const std::streamsize wide)
Definition logstream.cc:341
std::ostream & get_console()
Definition logstream.cc:239
unsigned int std_depth
Definition logstream.h:351
friend LogStream & operator<<(LogStream &log, const T &t)
Definition logstream.h:406
unsigned int file_depth
Definition logstream.h:356
std::ostream * std_out
Definition logstream.h:335
void push(const std::string &text)
Definition logstream.cc:302
bool print_thread_id
Definition logstream.h:361
Threads::ThreadLocalStorage< std::shared_ptr< std::ostringstream > > outstreams
Definition logstream.h:385
std::ios::fmtflags flags(const std::ios::fmtflags f)
Definition logstream.cc:325
void attach(std::ostream &o, const bool print_job_id=true, const std::ios_base::fmtflags flags=std::ios::showpoint|std::ios::left)
Definition logstream.cc:218
bool at_newline
Definition logstream.h:366
std::streamsize precision(const std::streamsize prec)
Definition logstream.cc:333
std::ostream & get_file_stream()
Definition logstream.cc:270
unsigned int depth_console(const unsigned int n)
Definition logstream.cc:349
std::ostringstream & get_stream()
Definition logstream.cc:247
bool has_file() const
Definition logstream.cc:281
unsigned int depth_file(const unsigned int n)
Definition logstream.cc:360
bool log_thread_id(const bool flag)
Definition logstream.cc:371
std::ostream * file
Definition logstream.h:343
Threads::ThreadLocalStorage< std::stack< std::string > > prefixes
Definition logstream.h:321
void print_line_head()
Definition logstream.cc:402
std::stack< std::string > & get_prefixes() const
Definition logstream.cc:382
const std::string & get_prefix() const
Definition logstream.cc:289
void detach()
Definition logstream.cc:231
void pop()
Definition logstream.cc:316
~LogStream() override
Definition logstream.cc:88
std::optional< T > get_for_thread(const std::thread::id &id) const
#define DEAL_II_NAMESPACE_OPEN
Definition config.h:498
#define DEAL_II_NAMESPACE_CLOSE
Definition config.h:499
#define Assert(cond, exc)
#define AssertNothrow(cond, exc)
static ::ExceptionBase & ExcMessage(std::string arg1)
LogStream deallog
Definition logstream.cc:36
LogStream deallog
Definition logstream.cc:36
STL namespace.