Reference documentation for deal.II version Git cb0bd54b52 2019-09-21 16:31:22 -0400
\(\newcommand{\dealcoloneq}{\mathrel{\vcenter{:}}=}\)
logstream.cc
1 // ---------------------------------------------------------------------
2 //
3 // Copyright (C) 1998 - 2018 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 
16 #include <deal.II/base/job_identifier.h>
17 #include <deal.II/base/logstream.h>
18 #include <deal.II/base/thread_management.h>
19 
20 #include <fstream>
21 #include <iomanip>
22 #include <iostream>
23 #include <sstream>
24 #include <thread>
25 
26 
27 DEAL_II_NAMESPACE_OPEN
28 
29 namespace
30 {
31  Threads::Mutex log_lock;
32  Threads::Mutex write_lock;
33 } // namespace
34 
35 
36 // The standard log object of deal.II:
37 LogStream deallog;
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  : 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().push("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().length() > 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)
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 
124 LogStream &
125 LogStream::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)
199  print_line_head();
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 && (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 
217 void
218 LogStream::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 
230 void
232 {
233  std::lock_guard<std::mutex> lock(log_lock);
234  file = nullptr;
235 }
236 
237 
238 std::ostream &
240 {
241  return *std_out;
242 }
243 
244 
245 
246 std::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 
269 std::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 
280 bool
282 {
283  return (file != nullptr);
284 }
285 
286 
287 
288 const 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 
301 void
302 LogStream::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 
315 void
317 {
318  if (get_prefixes().size() > 0)
319  get_prefixes().pop();
320 }
321 
322 
323 
324 std::ios::fmtflags
325 LogStream::flags(const std::ios::fmtflags f)
326 {
327  return get_stream().flags(f);
328 }
329 
330 
331 
332 std::streamsize
333 LogStream::precision(const std::streamsize prec)
334 {
335  return get_stream().precision(prec);
336 }
337 
338 
339 
340 std::streamsize
341 LogStream::width(const std::streamsize wide)
342 {
343  return get_stream().width(wide);
344 }
345 
346 
347 
348 unsigned int
349 LogStream::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 
359 unsigned int
360 LogStream::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 
370 bool
371 LogStream::log_thread_id(const bool flag)
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 
381 std::stack<std::string> &
383 {
384 #ifdef DEAL_II_WITH_THREADS
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  const tbb::enumerable_thread_specific<std::stack<std::string>> &impl =
394 
395  // The thread that created this LogStream object should be the first
396  // in tbb's enumerable_thread_specific container.
397  const tbb::enumerable_thread_specific<
398  std::stack<std::string>>::const_iterator first_elem = impl.begin();
399 
400  if (first_elem != impl.end())
401  {
402  local_prefixes = *first_elem;
403  }
404  }
405 
406  return local_prefixes;
407 
408 #else
409  return prefixes.get();
410 #endif
411 }
412 
413 
414 
415 void
417 {
418  const std::string & head = get_prefix();
419  const std::thread::id thread = std::this_thread::get_id();
420 
421  if (get_prefixes().size() <= std_depth)
422  {
423  if (print_thread_id)
424  *std_out << '[' << thread << ']';
425 
426  if (head.size() > 0)
427  *std_out << head << ':';
428  }
429 
430  if (file && (get_prefixes().size() <= file_depth))
431  {
432  if (print_thread_id)
433  *file << '[' << thread << ']';
434 
435  if (head.size() > 0)
436  *file << head << ':';
437  }
438 }
439 
440 DEAL_II_NAMESPACE_CLOSE
void pop()
Definition: logstream.cc:316
#define AssertNothrow(cond, exc)
Definition: exceptions.h:1471
std::streamsize precision(const std::streamsize prec)
Definition: logstream.cc:333
std::ostream * std_out
Definition: logstream.h:329
Threads::ThreadLocalStorage< std::stack< std::string > > prefixes
Definition: logstream.h:322
const std::string & get_prefix() const
Definition: logstream.cc:289
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 print_thread_id
Definition: logstream.h:355
~LogStream() override
Definition: logstream.cc:88
unsigned int depth_file(const unsigned int n)
Definition: logstream.cc:360
STL namespace.
unsigned int std_depth
Definition: logstream.h:345
std::streamsize width(const std::streamsize wide)
Definition: logstream.cc:341
bool log_thread_id(const bool flag)
Definition: logstream.cc:371
std::ostream & get_file_stream()
Definition: logstream.cc:270
std::ios::fmtflags flags(const std::ios::fmtflags f)
Definition: logstream.cc:325
static const JobIdentifier & get_dealjobid()
static ::ExceptionBase & ExcMessage(std::string arg1)
bool has_file() const
Definition: logstream.cc:281
#define Assert(cond, exc)
Definition: exceptions.h:1407
tbb::enumerable_thread_specific< T > & get_implementation()
std::stack< std::string > & get_prefixes() const
Definition: logstream.cc:382
unsigned int depth_console(const unsigned int n)
Definition: logstream.cc:349
void print_line_head()
Definition: logstream.cc:416
std::ostream * file
Definition: logstream.h:337
SmartPointer< LogStream, LogStream::Prefix > stream
Definition: logstream.h:128
Prefix(const std::string &text)
Definition: logstream.cc:41
std::ostream & get_console()
Definition: logstream.cc:239
bool at_newline
Definition: logstream.h:360
Threads::ThreadLocalStorage< std::shared_ptr< std::ostringstream > > outstreams
Definition: logstream.h:379
void push(const std::string &text)
Definition: logstream.cc:302
LogStream & operator<<(std::ostream &(*p)(std::ostream &))
Definition: logstream.cc:125
void detach()
Definition: logstream.cc:231
std::ostringstream & get_stream()
Definition: logstream.cc:247
unsigned int file_depth
Definition: logstream.h:350