Apply clang-format to source tree
[alexxy/gromacs.git] / src / gromacs / utility / logger.h
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2016,2017,2018,2019, by the GROMACS development team, led by
5  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6  * and including many others, as listed in the AUTHORS file in the
7  * top-level source directory and at http://www.gromacs.org.
8  *
9  * GROMACS is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public License
11  * as published by the Free Software Foundation; either version 2.1
12  * of the License, or (at your option) any later version.
13  *
14  * GROMACS is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with GROMACS; if not, see
21  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
23  *
24  * If you want to redistribute modifications to GROMACS, please
25  * consider that scientific software is very special. Version
26  * control is crucial - bugs must be traceable. We will be happy to
27  * consider code for inclusion in the official distribution, but
28  * derived work must not be called official GROMACS. Details are found
29  * in the README & COPYING files - if they are missing, get the
30  * official version at http://www.gromacs.org.
31  *
32  * To help us fund GROMACS development, we humbly ask that you cite
33  * the research papers on the package. Check out http://www.gromacs.org.
34  */
35 /*! \libinternal \file
36  * \brief
37  * Declares functionality for logging.
38  *
39  * See \ref page_logging for an overview of the functionality.
40  *
41  * \inlibraryapi
42  * \ingroup module_utility
43  */
44 #ifndef GMX_UTILITY_LOGGER_H
45 #define GMX_UTILITY_LOGGER_H
46
47 #include <string>
48
49 #include "gromacs/utility/stringutil.h"
50
51 namespace gmx
52 {
53
54 struct LogEntry
55 {
56     LogEntry() : asParagraph(false) {}
57
58     std::string text;
59     bool        asParagraph;
60 };
61
62 /*! \libinternal \brief
63  * Target where log output can be written.
64  *
65  * \ingroup module_utility
66  */
67 class ILogTarget
68 {
69 public:
70     virtual ~ILogTarget();
71
72     //! Writes a log entry to this target.
73     virtual void writeEntry(const LogEntry& entry) = 0;
74 };
75
76 /*! \libinternal \brief
77  * Helper class for creating log entries with ::GMX_LOG.
78  *
79  * \ingroup module_utility
80  */
81 class LogEntryWriter
82 {
83 public:
84     //! Appends given text as a line in the log entry.
85     LogEntryWriter& appendText(const char* text)
86     {
87         entry_.text.append(text);
88         return *this;
89     }
90     //! Appends given text as a line in the log entry.
91     LogEntryWriter& appendText(const std::string& text)
92     {
93         entry_.text.append(text);
94         return *this;
95     }
96     //! Appends given text as a line in the log entry, with printf-style formatting.
97     LogEntryWriter& appendTextFormatted(gmx_fmtstr const char* fmt, ...) gmx_format(printf, 2, 3);
98     //! Writes the log entry with empty lines before and after.
99     LogEntryWriter& asParagraph()
100     {
101         entry_.asParagraph = true;
102         return *this;
103     }
104
105 private:
106     LogEntry entry_;
107
108     friend class LogWriteHelper;
109 };
110
111 /*! \internal \brief
112  * Helper class for implementing ::GMX_LOG.
113  *
114  * \ingroup module_utility
115  */
116 class LogWriteHelper
117 {
118 public:
119     //! Initializes a helper for writing to the given target.
120     explicit LogWriteHelper(ILogTarget* target) : target_(target) {}
121
122     // Should be explicit, once that works in CUDA.
123     /*! \brief
124      * Returns whether anything needs to be written.
125      *
126      * Note that the return value is unintuitively `false` when the target
127      * is active, to allow implementing ::GMX_LOG like it is now.
128      */
129     operator bool() const { return target_ == nullptr; }
130
131     /*! \brief
132      * Writes the entry from the given writer to the log target.
133      *
134      * This is implemented as an assignment operator to get proper
135      * precedence for operations for the ::GMX_LOG macro; this is a common
136      * technique for implementing macros that allow streming information to
137      * them (see, e.g., Google Test).
138      */
139     LogWriteHelper& operator=(const LogEntryWriter& entryWriter)
140     {
141         target_->writeEntry(entryWriter.entry_);
142         return *this;
143     }
144
145 private:
146     ILogTarget* target_;
147 };
148
149 /*! \libinternal \brief
150  * Represents a single logging level.
151  *
152  * Typically this type is not used directly, but instances in MDLogger are
153  * simply accessed through ::GMX_LOG in code that writes to the log.
154  *
155  * \ingroup module_utility
156  */
157 class LogLevelHelper
158 {
159 public:
160     //! Initializes a helper for writing to the given target.
161     explicit LogLevelHelper(ILogTarget* target) : target_(target) {}
162
163     // Both of the below should be explicit, once that works in CUDA.
164     //! Returns whether the output for this log level goes anywhere.
165     operator bool() const { return target_ != nullptr; }
166
167     //! Creates a helper for ::GMX_LOG.
168     operator LogWriteHelper() const { return LogWriteHelper(target_); }
169
170 private:
171     ILogTarget* target_;
172 };
173
174 /*! \libinternal \brief
175  * Declares a logging interface.
176  *
177  * Typically, this object is not created directly, but instead through
178  * LoggerBuilder.
179  *
180  * For now, this is named MDLogger, since it is used only there, and it is not
181  * clear whether the logging levels can be the same throughout the code.  It
182  * should be relatively straightforward to split this into multiple classes
183  * with different supported logging levels without changing calling code, or to
184  * rename it to Logger if we do not need any specialization.
185  *
186  * \ingroup module_utility
187  */
188 class MDLogger
189 {
190 public:
191     //! Supported logging levels.
192     enum class LogLevel
193     {
194         Error,
195         Warning,
196         Info,
197         Debug,
198         VerboseDebug,
199         Count
200     };
201     //! Number of logging levels.
202     static const int LogLevelCount = static_cast<int>(LogLevel::Count);
203
204     MDLogger();
205     //! Creates a logger with the given targets.
206     explicit MDLogger(ILogTarget* targets[LogLevelCount]);
207
208     //! For writing at LogLevel::Warning level.
209     LogLevelHelper warning;
210     //! For writing at LogLevel::Error level.
211     LogLevelHelper error;
212     //! For writing at LogLevel::Debug level.
213     LogLevelHelper debug;
214     //! For writing at LogLevel::VerboseDebug level.
215     LogLevelHelper verboseDebug;
216     //! For writing at LogLevel::Info level.
217     LogLevelHelper info;
218 };
219
220 /*! \brief
221  * Helper to log information using gmx::MDLogger.
222  *
223  * \param  logger  LogLevelHelper instance to use for logging.
224  *
225  * Used as
226  * \code
227    GMX_LOG(logger.warning).appendText(...);
228    \endcode
229  * and ensures that the code to format the output is only executed when the
230  * output goes somewhere.
231  *
232  * See LogEntryWriter for functions that can be used with the macro (such as
233  * the appendText() in the example).
234  *
235  * \ingroup module_utility
236  */
237 #define GMX_LOG(logger)                                                  \
238     if (::gmx::LogWriteHelper helper = ::gmx::LogWriteHelper(logger)) {} \
239     else                                                                 \
240         helper = ::gmx::LogEntryWriter()
241
242 } // namespace gmx
243
244 #endif