Coverage Report

Created: 2026-08-31 06:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/openbabel/src/oberror.cpp
Line
Count
Source
1
/**********************************************************************
2
oberror.cpp - Handle error messages.
3
4
Copyright (C) 2002 by Stefan Kebekus
5
Some portions Copyright (C) 2002-2006 by Geoffrey R. Hutchison
6
7
This file is part of the Open Babel project.
8
For more information, see <http://openbabel.org/>
9
10
This program is free software; you can redistribute it and/or modify
11
it under the terms of the GNU General Public License as published by
12
the Free Software Foundation version 2 of the License.
13
14
This program is distributed in the hope that it will be useful, but
15
WITHOUT ANY WARRANTY; without even the implied warranty of
16
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17
General Public License for more details.
18
***********************************************************************/
19
20
#include <openbabel/babelconfig.h>
21
22
#include <iostream>
23
#include <string>
24
#include <algorithm>
25
26
#include <openbabel/oberror.h>
27
28
using namespace std;
29
30
namespace OpenBabel
31
{
32
  // Initialize the global obErrorLog declared in oberror.h
33
  OBMessageHandler obErrorLog;
34
35
  OBError::OBError( const string &method,
36
                    const string &errorMsg,
37
                    const string &explanation,
38
                    const string &possibleCause,
39
                    const string &suggestedRemedy,
40
                    const obMessageLevel level) :
41
3.92M
    _method(method), _errorMsg(errorMsg), _explanation(explanation),
42
3.92M
    _possibleCause(possibleCause), _suggestedRemedy(suggestedRemedy),
43
3.92M
    _level(level)
44
3.92M
  { }
45
46
  string OBError::message() const
47
0
  {
48
0
    string tmp = "==============================\n";
49
50
0
    if (_level == obError)
51
0
      tmp += "*** Open Babel Error ";
52
0
    else if (_level == obWarning)
53
0
      tmp += "*** Open Babel Warning ";
54
0
    else if (_level == obInfo)
55
0
      tmp += "*** Open Babel Information ";
56
0
    else if (_level == obAuditMsg)
57
0
      tmp += "*** Open Babel Audit Log ";
58
0
    else
59
0
      tmp += "*** Open Babel Debugging Message ";
60
61
0
    if (_method.length() != 0)
62
0
      {
63
0
        tmp += " in " + _method + string("\n  ");
64
0
      }
65
0
    tmp += _errorMsg + "\n";
66
0
    if (!_explanation.empty())
67
0
      tmp += "  " + _explanation + "\n";
68
0
    if (!_possibleCause.empty())
69
0
      tmp += "  Possible reason: " + _possibleCause + "\n";
70
0
    if (!_suggestedRemedy.empty())
71
0
      tmp += "  Suggestion: " + _suggestedRemedy + "\n";
72
0
    return tmp;
73
0
  }
74
75
0
  bool OBError::operator== (const OBError& other)const {return GetError()==other.GetError();}
76
77
  /** \class OBMessageHandler oberror.h <openbabel/oberror.h>
78
79
  OBMessageHandler represents a configurable error system for Open Babel.
80
81
  A global error log is defined by the Open Babel library for use of
82
  internal code as well as code built on top of Open Babel. This class
83
  allows flexible filtering based on urgency (defined by the
84
  obMessageLevel type), an "audit log" of molecular changes, including
85
  recall using the GetMessagesOfLevel method, etc.
86
87
  The default is to only log and output errors of priority
88
  obMessageLevel::obError or obMessageLevel::obWarning.
89
90
  Long-running code may wish to set the size of the in-memory error log
91
  using the StartLogging / StopLogging methods and
92
  SetMaxLogEntries. Otherwise, the error log may easily fill up,
93
  requiring large amounts of memory.
94
95
  If you wish to divert error output to a different std::ostream (i.e.,
96
  for graphical display, or a file log), use the SetOutputStream method
97
  -- the default goes to the std::clog stream. Furthermore, some older
98
  code uses std::cerr for direct error output, rather than the
99
  ThrowError() methods in this class. To prevent this, you can turn on
100
  "error wrapping" using the StartErrorWrap method -- this behavior is
101
  turned off by default.
102
103
  To make it easy to use the OBMessageHandler class and error logging
104
  facilities, a global log is defined:
105
106
  \code
107
  OBERROR extern OBMessageHandler obErrorLog;
108
  \endcode
109
110
  Therefore, it is very easy to log errors:
111
112
  \code
113
  if (atomIndex < 1 || atomIndex > mol.NumAtoms() )
114
     obErrorLog.ThrowError(__FUNCTION__, "Requested Atom Out of Range", obDebug);
115
  \endcode
116
117
  or
118
119
  \code
120
  stringstream errorMsg;
121
  errorMsg << " Could not parse line in type translation table types.txt -- incorect number of columns";
122
  errorMsg << " found " << vc.size() << " expected " << _ncols << ".";
123
  obErrorLog.ThrowError(__FUNCTION__, errorMsg.str(), obInfo);
124
  \endcode
125
126
  The __FUNCTION__ builtin is defined by many compilers (e.g., <a
127
  href="http://gcc.gnu.org/">GCC</a>) but can be defined to an empty
128
  string on some platforms without this compiler extension.
129
130
  Output from the error log typically looks like:
131
  \code
132
  ==============================
133
  *** Open Babel Audit Log  in ReadChemObject
134
  OpenBabel::Read molecule Protein Data Bank format
135
  ==============================
136
  *** Open Babel Information  in ParseConectRecord
137
  WARNING: Problems reading a PDB file
138
  Problems reading a CONECT record.
139
  According to the PDB specification,
140
  the record should have 70 columns, but OpenBabel found 61 columns.
141
  \endcode
142
143
  **/
144
145
  OBMessageHandler::OBMessageHandler() :
146
2
    _outputLevel(obWarning), _outputStream(&clog), _logging(true), _maxEntries(100)
147
2
  {
148
2
    _messageCount[0] = _messageCount[1] = _messageCount[2] = 0;
149
2
    _messageCount[3] = _messageCount[4] = 0;
150
2
    _filterStreamBuf = _inWrapStreamBuf = nullptr;
151
    //  StartErrorWrap(); // (don't turn on error wrapping by default)
152
2
  }
153
154
  OBMessageHandler::~OBMessageHandler()
155
0
  {
156
0
    StopErrorWrap();
157
158
    // free the internal filter streambuf
159
0
    delete _filterStreamBuf;
160
0
  }
161
162
  void OBMessageHandler::ThrowError(OBError err, errorQualifier qualifier)
163
3.92M
  {
164
3.92M
    if (!_logging)
165
3.92M
      return;
166
167
    //Output error message if level sufficiently high and, if onceOnly set, it has not been logged before
168
0
    if (err.GetLevel() <= _outputLevel &&
169
0
      (qualifier!=onceOnly || find(_messageList.begin(), _messageList.end(), err)==_messageList.end()))
170
0
    {
171
0
      *_outputStream << err;
172
0
    }
173
174
0
    _messageList.push_back(err);
175
0
    _messageCount[err.GetLevel()]++;
176
0
    if (_maxEntries != 0 && _messageList.size() > _maxEntries)
177
0
      _messageList.pop_front();
178
0
  }
179
180
  void OBMessageHandler::ThrowError(const std::string &method,
181
                                    const std::string &errorMsg,
182
                                    obMessageLevel level, errorQualifier qualifier)
183
3.92M
  {
184
3.92M
    if (errorMsg.length() > 1)
185
3.92M
      {
186
3.92M
        OBError err(method, errorMsg, "", "", "", level);
187
3.92M
        ThrowError(err, qualifier);
188
3.92M
      }
189
3.92M
  }
190
191
  std::vector<std::string> OBMessageHandler::GetMessagesOfLevel(const obMessageLevel level)
192
0
  {
193
0
    vector<string> results;
194
0
    deque<OBError>::iterator i;
195
0
    OBError error;
196
197
0
    for (i = _messageList.begin(); i != _messageList.end(); ++i)
198
0
      {
199
0
        error = (*i);
200
0
        if (error.GetLevel() == level)
201
0
          results.push_back( error.message() );
202
0
      }
203
204
0
    return results;
205
0
  }
206
207
  bool OBMessageHandler::StartErrorWrap()
208
0
  {
209
0
    if (_inWrapStreamBuf != nullptr)
210
0
      return true; // already wrapped cerr  -- don't go into loops!
211
212
0
    _inWrapStreamBuf = cerr.rdbuf();
213
214
0
    if (_filterStreamBuf == nullptr)
215
0
      {
216
0
        _filterStreamBuf = new(obLogBuf);
217
0
      }
218
219
0
    cerr.rdbuf(_filterStreamBuf);
220
0
    return true;
221
0
  }
222
223
  bool OBMessageHandler::StopErrorWrap()
224
0
  {
225
0
    if (_inWrapStreamBuf == nullptr)
226
0
      return true; // never wrapped cerr
227
228
0
    cerr.rdbuf(_inWrapStreamBuf);
229
0
    _inWrapStreamBuf = nullptr; //shows not wrapped
230
231
    // don't delete the filter streambuf yet -- we might start wrapping later
232
    // it's freed in the dtor
233
234
0
    return true;
235
0
  }
236
237
  string OBMessageHandler::GetMessageSummary()
238
0
  {
239
0
    stringstream summary;
240
0
    if (_messageCount[obError] > 0)
241
0
      summary << _messageCount[obError] << " errors ";
242
0
    if (_messageCount[obWarning] > 0)
243
0
      summary << _messageCount[obWarning] << " warnings ";
244
0
    if (_messageCount[obInfo] > 0)
245
0
      summary << _messageCount[obInfo] << " info messages ";
246
0
    if (_messageCount[obAuditMsg] > 0)
247
0
      summary << _messageCount[obAuditMsg] << " audit log messages ";
248
0
    if (_messageCount[obDebug] > 0)
249
0
      summary << _messageCount[obDebug] << " debugging messages ";
250
251
0
    return summary.str();
252
0
  }
253
254
} // end namespace OpenBabel
255
256
//! \file oberror.cpp
257
//! \brief Handle error messages, warnings, notices, etc.
258
//!  Implements OBMessageHandler class.