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