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