File: logstream.cc

package info (click to toggle)
deal.ii 9.0.1-1
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 181,876 kB
  • sloc: cpp: 265,739; ansic: 52,054; python: 1,507; perl: 645; sh: 506; xml: 437; makefile: 73
file content (436 lines) | stat: -rw-r--r-- 9,869 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
// ---------------------------------------------------------------------
//
// Copyright (C) 1998 - 2018 by the deal.II authors
//
// This file is part of the deal.II library.
//
// The deal.II library is free software; you can use it, redistribute
// it, and/or modify it under the terms of the GNU Lesser General
// Public License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
// The full text of the license can be found in the file LICENSE at
// the top level of the deal.II distribution.
//
// ---------------------------------------------------------------------

#include <deal.II/base/logstream.h>
#include <deal.II/base/job_identifier.h>
#include <deal.II/base/thread_management.h>

#include <iostream>
#include <iomanip>
#include <fstream>
#include <sstream>


DEAL_II_NAMESPACE_OPEN

namespace
{
  Threads::Mutex log_lock;
  Threads::Mutex write_lock;
}


// The standard log object of deal.II:
LogStream deallog;




LogStream::Prefix::Prefix(const std::string &text)
  :
  stream(&deallog)
{
  stream->push(text);
}



LogStream::Prefix::Prefix(const std::string &text,
                          LogStream &s)
  :
  stream(&s)
{
  stream->push(text);
}



LogStream::Prefix::~Prefix()
{
  // destructors may not throw exceptions. if the pop() function
  // actually does throw one, then ignore it
  try
    {
      stream->pop();
    }
  catch (...)
    {
      AssertNothrow (false,
                     ExcMessage("An exception occurred in LogStream::Prefix::~Prefix."));
    }
}



LogStream::LogStream()
  :
  std_out(&std::cout),
  file(nullptr),
  std_depth(0),
  file_depth(10000),
  print_thread_id(false),
  at_newline(true)
{
  get_prefixes().push("DEAL:");
}



LogStream::~LogStream()
{
  // if there was anything left in the stream that is current to this
  // thread, make sure we flush it before it gets lost
  {
    if (get_stream().str().length() > 0)
      {
        // except the situation is not quite that simple. if this object is
        // the 'deallog' object, then it is destroyed upon exit of the
        // program. since it's defined in a shared library that depends on
        // libstdc++.so, destruction happens before destruction of
        // std::cout/cerr, but after all file variables defined in user
        // programs have been destroyed. in other words, if we get here and
        // the object being destroyed is 'deallog' and if 'deallog' is
        // associated with a file stream, then we're in trouble: we'll try
        // to write to a file that doesn't exist any more, and we're likely
        // going to crash (this is tested by base/log_crash_01). rather
        // than letting it come to this, print a message to the screen
        // (note that we can't issue an assertion here either since Assert
        // may want to write to 'deallog' itself, and AssertThrow will
        // throw an exception that can't be caught)
        if ((this == &deallog) && file)
          *std_out << ("You still have content that was written to 'deallog' "
                       "but not flushed to the screen or a file while the "
                       "program is being terminated. This would lead to a "
                       "segmentation fault. Make sure you flush the "
                       "content of the 'deallog' object using 'std::endl' "
                       "before the end of the program.")
                   << std::endl;
        else
          *this << std::endl;
      }
  }
}


LogStream &
LogStream::operator<< (std::ostream& (*p) (std::ostream &))
{

  std::ostringstream &stream = get_stream();

  // Print to the internal stringstream:
  stream << p;


  // This is a bloody hack until LogStream got reimplemented as a proper
  // child of std::streambuf (or similar).
  //
  // The problem is that at this point we would like to know whether an
  // std::flush or std::endl has called us, however, there is no way to
  // detect this in a sane manner.
  //
  // The obvious idea to compare function pointers,
  //   std::ostream & (* const p_flush) (std::ostream &) = &std::flush;
  //   p == p_flush ? ...,
  // is wrong as there doesn't has to be a _single_ std::flush instance...
  // there could be multiple of it. And in fact, LLVM's libc++ implements
  // std::flush and std::endl in a way that every shared library and
  // executable has its local copy... fun...
  //
  // - Maier, 2013

  class QueryStreambuf : public std::streambuf
  {
    // Implement a minimalistic stream buffer that only stores the fact
    // whether overflow or sync was called
  public:
    QueryStreambuf()
      : flushed_(false), newline_written_(false)
    {
    }
    bool flushed()
    {
      return flushed_;
    }
    bool newline_written()
    {
      return newline_written_;
    }
  private:
    int_type overflow(int_type ch)
    {
      newline_written_ = true;
      return ch;
    }
    int sync()
    {
      flushed_ = true;
      return 0;
    }
    bool flushed_;
    bool newline_written_;
  } query_streambuf;

  {
    // and initialize an ostream with this streambuf:
    std::ostream inject (&query_streambuf);
    inject << p;
  }

  if (query_streambuf.flushed())
    {
      Threads::Mutex::ScopedLock lock(write_lock);

      // Print the line head in case of a previous newline:
      if (at_newline)
        print_line_head();

      at_newline = query_streambuf.newline_written();

      if (get_prefixes().size() <= std_depth)
        *std_out << stream.str();

      if (file && (get_prefixes().size() <= file_depth))
        *file << stream.str() << std::flush;

      // Start a new string:
      stream.str("");
    }

  return *this;
}


void
LogStream::attach(std::ostream &o,
                  const bool    print_job_id)
{
  Threads::Mutex::ScopedLock lock(log_lock);
  file = &o;
  o.setf(std::ios::showpoint | std::ios::left);
  if (print_job_id)
    o << dealjobid();
}


void LogStream::detach ()
{
  Threads::Mutex::ScopedLock lock(log_lock);
  file = nullptr;
}


std::ostream &
LogStream::get_console()
{
  return *std_out;
}



std::ostringstream &
LogStream::get_stream()
{
  // see if we have already created this stream. if not, do so and
  // set the default flags (why we set these flags is lost to
  // history, but this is what we need to keep several hundred tests
  // from producing different output)
  //
  // note that in all of this we need not worry about thread-safety
  // because we operate on a thread-local object and by definition
  // there can only be one access at a time
  if (outstreams.get().get() == nullptr)
    {
      outstreams.get() = std::make_shared<std::ostringstream>();
      outstreams.get()->setf(std::ios::showpoint | std::ios::left);
    }

  // then return the stream
  return *outstreams.get();
}



std::ostream &
LogStream::get_file_stream()
{
  Assert(file,
         ExcMessage("You can't ask for the std::ostream object for the output "
                    "file if none had been set before."));
  return *file;
}



bool
LogStream::has_file() const
{
  return (file != nullptr);
}



const std::string &
LogStream::get_prefix() const
{
  static std::string empty_string;

  if (get_prefixes().size() > 0)
    return get_prefixes().top();
  else
    return empty_string;
}



void
LogStream::push (const std::string &text)
{
  std::string pre;
  if (get_prefixes().size() > 0)
    pre = get_prefixes().top();

  pre += text;
  pre += std::string(":");
  get_prefixes().push(pre);
}



void LogStream::pop ()
{
  if (get_prefixes().size() > 0)
    get_prefixes().pop();
}



std::ios::fmtflags
LogStream::flags(const std::ios::fmtflags f)
{
  return get_stream().flags (f);
}



std::streamsize
LogStream::precision (const std::streamsize prec)
{
  return get_stream().precision (prec);
}



std::streamsize
LogStream::width (const std::streamsize wide)
{
  return get_stream().width (wide);
}



unsigned int
LogStream::depth_console (const unsigned int n)
{
  Threads::Mutex::ScopedLock lock(log_lock);
  const unsigned int h = std_depth;
  std_depth = n;
  return h;
}



unsigned int
LogStream::depth_file (const unsigned int n)
{
  Threads::Mutex::ScopedLock lock(log_lock);
  const unsigned int h = file_depth;
  file_depth = n;
  return h;
}



bool
LogStream::log_thread_id (const bool flag)
{
  Threads::Mutex::ScopedLock lock(log_lock);
  const bool h = print_thread_id;
  print_thread_id = flag;
  return h;
}



std::stack<std::string> &
LogStream::get_prefixes() const
{
#ifdef DEAL_II_WITH_THREADS
  bool exists = false;
  std::stack<std::string> &local_prefixes = prefixes.get(exists);

  // If this is a new locally stored stack, copy the "blessed" prefixes
  // from the initial thread that created logstream.
  if (! exists)
    {
      const tbb::enumerable_thread_specific<std::stack<std::string> > &impl
        = prefixes.get_implementation();

      // The thread that created this LogStream object should be the first
      // in tbb's enumerable_thread_specific container.
      const tbb::enumerable_thread_specific<std::stack<std::string> >::const_iterator first_elem
        = impl.begin();

      if (first_elem != impl.end())
        {
          local_prefixes = *first_elem;
        }
    }

  return local_prefixes;

#else
  return prefixes.get();
#endif
}



void
LogStream::print_line_head()
{
  const std::string &head = get_prefix();
  const unsigned int thread = Threads::this_thread_id();

  if (get_prefixes().size() <= std_depth)
    {
      if (print_thread_id)
        *std_out << '[' << thread << ']';

      if (head.size() > 0)
        *std_out <<  head << ':';
    }

  if (file && (get_prefixes().size() <= file_depth))
    {
      if (print_thread_id)
        *file << '[' << thread << ']';

      if (head.size() > 0)
        *file << head << ':';
    }
}

DEAL_II_NAMESPACE_CLOSE