Skip to content

Commit 889ea4e

Browse files
authored
feat: Adding error YAML file in GEOS (#3828)
- provide a file that stores warnings and errors in a structured format (YAML) - start a refactor to centralize the error macros & systems
1 parent 64293f1 commit 889ea4e

File tree

130 files changed

+2345
-549
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

130 files changed

+2345
-549
lines changed

src/coreComponents/common/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ Also provides commonly used components for such as logging, formatting, memory a
1919
dependencies.
2020
#]]
2121

22-
2322
#
2423
# Specify all headers
2524
#
@@ -41,6 +40,7 @@ set( common_headers
4140
GeosxMacros.hpp
4241
MemoryInfos.hpp
4342
logger/Logger.hpp
43+
logger/ErrorHandling.hpp
4444
MpiWrapper.hpp
4545
Path.hpp
4646
Span.hpp
@@ -76,6 +76,7 @@ set( common_sources
7676
format/LogPart.cpp
7777
format/StringUtilities.cpp
7878
logger/Logger.cpp
79+
logger/ErrorHandling.cpp
7980
BufferAllocator.cpp
8081
MemoryInfos.cpp
8182
MpiWrapper.cpp

src/coreComponents/common/GeosxMacros.hpp

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,4 +133,71 @@ void i_g_n_o_r_e( ARGS const & ... ) {}
133133
#endif
134134
#endif
135135

136+
/**
137+
* @name Parameters processing internal macros
138+
*
139+
* These internal macros allow to craft macros with multiple count of parameters.
140+
*/
141+
///@{
142+
143+
/// internal macro for GEOS_DETAIL_MORE_THAN_ONE_ARG
144+
#define GEOS_DETAIL_MORE_THAN_ONE_ARG_VALUE( _00, _01, _02, _03, _04, _05, _06, _07, \
145+
_08, _09, _10, _11, _12, _13, _14, _15, \
146+
_INDEX, ... ) _INDEX
147+
148+
/**
149+
* @return 1 if variadic argument has more than 1 element, 0 otherwise.
150+
* @note Undefined behaviour if variadic argument has more that 16 elements.
151+
*/
152+
#define GEOS_DETAIL_MORE_THAN_ONE_ARG( ... ) \
153+
GEOS_DETAIL_MORE_THAN_ONE_ARG_VALUE( __VA_ARGS__, \
154+
true, true, true, true, true, true, true, true, \
155+
true, true, true, true, true, true, true, false, false )
156+
157+
/// @cond DO_NOT_DOCUMENT
158+
159+
/// internal macros for GEOS_DETAIL_FIRST_ARG
160+
#define GEOS_DETAIL_FIRST_ARG_false( FIRST ) FIRST
161+
#define GEOS_DETAIL_FIRST_ARG_true( FIRST, ... ) FIRST
162+
#define GEOS_DETAIL_FIRST_ARG_FUNC( COND ) GEOS_DETAIL_FIRST_ARG_ ## COND
163+
#define GEOS_DETAIL_FIRST_ARG_DISPATCH( COND, ... ) GEOS_DETAIL_FIRST_ARG_FUNC( COND )(__VA_ARGS__)
164+
165+
/// internal macros for GEOS_DETAIL_LAST_ARG
166+
#define GEOS_DETAIL_REST_ARGS_false( FIRST )
167+
#define GEOS_DETAIL_REST_ARGS_true( FIRST, ... ) __VA_ARGS__
168+
#define GEOS_DETAIL_REST_ARGS_FUNC( COND ) GEOS_DETAIL_REST_ARGS_ ## COND
169+
#define GEOS_DETAIL_REST_ARGS_DISPATCH( COND, ... ) GEOS_DETAIL_REST_ARGS_FUNC( COND )(__VA_ARGS__)
170+
171+
/// internal macros for GEOS_DETAIL_LAST_ARG_PREP
172+
#define GEOS_DETAIL_REST_PREP_ARGS_false( FIRST )
173+
#define GEOS_DETAIL_REST_PREP_ARGS_true( FIRST, ... ) , __VA_ARGS__
174+
#define GEOS_DETAIL_REST_PREP_ARGS_FUNC( COND ) GEOS_DETAIL_REST_PREP_ARGS_ ## COND
175+
#define GEOS_DETAIL_REST_PREP_ARGS_DISPATCH( COND, ... ) GEOS_DETAIL_REST_PREP_ARGS_FUNC( COND )(__VA_ARGS__)
176+
177+
/// @endcond
178+
179+
/**
180+
* @return Return the first parameter of the variadic parameters (__VA_ARGS__).
181+
* @note Undefined behaviour if variadic argument has more that 16 elements.
182+
*/
183+
#define GEOS_DETAIL_FIRST_ARG( ... ) GEOS_DETAIL_FIRST_ARG_DISPATCH( GEOS_DETAIL_MORE_THAN_ONE_ARG( __VA_ARGS__ ), \
184+
__VA_ARGS__ )
185+
186+
/**
187+
* @return Return the parameters following the first of the variadic parameters (__VA_ARGS__).
188+
* @note Undefined behaviour if variadic argument has more that 16 elements.
189+
*/
190+
#define GEOS_DETAIL_REST_ARGS( ... ) GEOS_DETAIL_REST_ARGS_DISPATCH( GEOS_DETAIL_MORE_THAN_ONE_ARG( __VA_ARGS__ ), \
191+
__VA_ARGS__ )
192+
193+
/**
194+
* @return Return the parameters following the first of the variadic parameters (__VA_ARGS__),
195+
* prepended with a comma when not empty.
196+
* @note Undefined behaviour if variadic argument has more that 16 elements.
197+
*/
198+
#define GEOS_DETAIL_REST_PREP_ARGS( ... ) GEOS_DETAIL_REST_PREP_ARGS_DISPATCH( GEOS_DETAIL_MORE_THAN_ONE_ARG( __VA_ARGS__ ), \
199+
__VA_ARGS__ )
200+
201+
///@}
202+
136203
#endif // GEOS_COMMON_GEOSXMACROS_HPP_
Lines changed: 261 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,261 @@
1+
/*
2+
* ------------------------------------------------------------------------------------------------------------
3+
* SPDX-License-Identifier: LGPL-2.1-only
4+
*
5+
* Copyright (c) 2016-2024 Lawrence Livermore National Security LLC
6+
* Copyright (c) 2018-2024 TotalEnergies
7+
* Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University
8+
* Copyright (c) 2023-2024 Chevron
9+
* Copyright (c) 2019- GEOS/GEOSX Contributors
10+
* All rights reserved
11+
*
12+
* See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details.
13+
* ------------------------------------------------------------------------------------------------------------
14+
*/
15+
16+
/**
17+
* @file ErrorHandling.cpp
18+
*/
19+
20+
#include "ErrorHandling.hpp"
21+
#include "common/logger/Logger.hpp"
22+
#include "common/format/StringUtilities.hpp"
23+
24+
#include <fstream>
25+
#include <regex>
26+
#include <string_view>
27+
28+
namespace geos
29+
{
30+
static constexpr std::string_view g_level1Start = " - ";
31+
static constexpr std::string_view g_level1Next = " ";
32+
// static constexpr std::string_view g_level2Start = " - "; // unused for now
33+
static constexpr std::string_view g_level2Next = " ";
34+
static constexpr std::string_view g_level3Start = " - ";
35+
static constexpr std::string_view g_level3Next = " ";
36+
37+
ErrorLogger g_errorLogger{};
38+
39+
ErrorLogger & ErrorLogger::global()
40+
{ return g_errorLogger; }
41+
42+
void ErrorLogger::createFile()
43+
{
44+
if( stringutilities::endsWith( m_filename, ".yaml" ) )
45+
{
46+
std::ofstream yamlFile( std::string( m_filename ), std::ios::out );
47+
if( yamlFile.is_open() )
48+
{
49+
yamlFile << "errors: \n\n";
50+
yamlFile.close();
51+
}
52+
else
53+
{
54+
GEOS_LOG_RANK( GEOS_FMT( "Unable to open error file for writing: {}", m_filename ) );
55+
}
56+
}
57+
else
58+
{
59+
enableFileOutput( false );
60+
GEOS_LOG_RANK( GEOS_FMT( "{} is a bad file name argument. The file must be in yaml format.", m_filename ) );
61+
}
62+
}
63+
64+
std::string ErrorLogger::ErrorContext::attributeToString( ErrorLogger::ErrorContext::Attribute attribute )
65+
{
66+
switch( attribute )
67+
{
68+
case ErrorLogger::ErrorContext::Attribute::InputFile: return "inputFile";
69+
case ErrorLogger::ErrorContext::Attribute::InputLine: return "inputLine";
70+
case ErrorLogger::ErrorContext::Attribute::DataPath: return "dataPath";
71+
default: return "Unknown";
72+
}
73+
}
74+
75+
ErrorLogger::ErrorMsg & ErrorLogger::ErrorMsg::addToMsg( std::exception const & e, bool toEnd )
76+
{
77+
if( toEnd )
78+
{
79+
m_msg = m_msg + e.what();
80+
}
81+
else
82+
{
83+
m_msg = e.what() + m_msg;
84+
}
85+
return *this;
86+
}
87+
88+
ErrorLogger::ErrorMsg & ErrorLogger::ErrorMsg::addToMsg( std::string_view errorMsg, bool toEnd )
89+
{
90+
if( toEnd )
91+
{
92+
m_msg = m_msg + std::string( errorMsg );
93+
}
94+
else
95+
{
96+
m_msg = std::string( errorMsg ) + m_msg;
97+
}
98+
return *this;
99+
}
100+
101+
ErrorLogger::ErrorMsg & ErrorLogger::ErrorMsg::setCodeLocation( std::string_view msgFile, integer msgLine )
102+
{
103+
m_file = msgFile;
104+
m_line = msgLine;
105+
return *this;
106+
}
107+
108+
ErrorLogger::ErrorMsg & ErrorLogger::ErrorMsg::setType( ErrorLogger::MsgType msgType )
109+
{
110+
m_type = msgType;
111+
return *this;
112+
}
113+
114+
ErrorLogger::ErrorMsg & ErrorLogger::ErrorMsg::setCause( std::string_view cause )
115+
{
116+
m_cause = cause;
117+
return *this;
118+
}
119+
120+
void ErrorLogger::ErrorMsg::addContextInfoImpl( ErrorLogger::ErrorContext && ctxInfo )
121+
{
122+
m_contextsInfo.emplace_back( std::move( ctxInfo ) );
123+
}
124+
125+
ErrorLogger::ErrorMsg & ErrorLogger::ErrorMsg::addRank( int rank )
126+
{
127+
m_ranksInfo.emplace( rank );
128+
return *this;
129+
}
130+
131+
ErrorLogger::ErrorMsg & ErrorLogger::ErrorMsg::addCallStackInfo( std::string_view ossStackTrace )
132+
{
133+
std::string str = std::string( ossStackTrace );
134+
std::istringstream iss( str );
135+
std::string stackLine;
136+
std::size_t index;
137+
138+
std::regex pattern( R"(Frame \d+: \S+)" );
139+
140+
while( std::getline( iss, stackLine ) )
141+
{
142+
if( std::regex_search( stackLine, pattern ))
143+
{
144+
m_isValidStackTrace = true;
145+
index = stackLine.find( ':' );
146+
m_sourceCallStack.push_back( stackLine.substr( index + 1 ) );
147+
}
148+
}
149+
150+
if( !m_isValidStackTrace )
151+
{
152+
m_sourceCallStack.push_back( str );
153+
}
154+
155+
return *this;
156+
}
157+
158+
std::string ErrorLogger::toString( ErrorLogger::MsgType type )
159+
{
160+
switch( type )
161+
{
162+
case ErrorLogger::MsgType::Error: return "Error";
163+
case ErrorLogger::MsgType::Warning: return "Warning";
164+
case ErrorLogger::MsgType::Exception: return "Exception";
165+
default: return "Unknown";
166+
}
167+
}
168+
169+
void ErrorLogger::streamMultilineYamlAttribute( std::string_view msg, std::ofstream & yamlFile,
170+
std::string_view indent )
171+
{
172+
std::size_t i = 0;
173+
// Loop that runs through the string_view named msg
174+
while( i < msg.size() )
175+
{
176+
// Index of the next line break
177+
std::size_t index = msg.find( "\n", i );
178+
// If there is no line break, the entire string is taken
179+
if( index == std::string_view::npos )
180+
{
181+
index = msg.size();
182+
}
183+
// Writes the current line to the YAML file with the desired indentation
184+
std::string_view msgLine = msg.substr( i, index - i );
185+
yamlFile << indent << msgLine << "\n";
186+
// Move to the next line
187+
i = index + 1;
188+
}
189+
}
190+
191+
void ErrorLogger::flushErrorMsg( ErrorLogger::ErrorMsg & errorMsg )
192+
{
193+
std::ofstream yamlFile( std::string( m_filename ), std::ios::app );
194+
if( yamlFile.is_open() && isOutputFileEnabled() )
195+
{
196+
// General errors info (type, rank on which the error occured)
197+
yamlFile << g_level1Start << "type: " << ErrorLogger::toString( errorMsg.m_type ) << "\n";
198+
yamlFile << g_level1Next << "rank: " << stringutilities::join( errorMsg.m_ranksInfo, "," );
199+
yamlFile << "\n";
200+
201+
// Error message
202+
yamlFile << g_level1Next << "message: >-\n";
203+
streamMultilineYamlAttribute( errorMsg.m_msg, yamlFile, g_level2Next );
204+
205+
// context information
206+
if( !errorMsg.m_contextsInfo.empty() )
207+
{
208+
// Sort contextual information by decreasing priority
209+
std::sort( errorMsg.m_contextsInfo.begin(), errorMsg.m_contextsInfo.end(),
210+
[]( const ErrorLogger::ErrorContext & a, const ErrorLogger::ErrorContext & b ) {
211+
return a.m_priority > b.m_priority;
212+
} );
213+
// Additional informations about the context of the error and priority information of each context
214+
yamlFile << g_level1Next << "contexts:\n";
215+
for( ErrorContext const & ctxInfo : errorMsg.m_contextsInfo )
216+
{
217+
yamlFile << g_level3Start << "priority: " << ctxInfo.m_priority << "\n";
218+
for( auto const & [key, value] : ctxInfo.m_attributes )
219+
{
220+
yamlFile << g_level3Next << ErrorContext::attributeToString( key ) << ": " << value << "\n";
221+
}
222+
}
223+
}
224+
225+
// error cause
226+
if( !errorMsg.m_cause.empty() )
227+
{
228+
yamlFile << g_level1Next << "cause: >-\n";
229+
streamMultilineYamlAttribute( errorMsg.m_cause, yamlFile, g_level2Next );
230+
}
231+
232+
// Location of the error in the code
233+
yamlFile << g_level1Next << "sourceLocation:\n";
234+
yamlFile << g_level2Next << "file: " << errorMsg.m_file << "\n";
235+
yamlFile << g_level2Next << "line: " << errorMsg.m_line << "\n";
236+
237+
// Information about the stack trace
238+
if( !errorMsg.m_sourceCallStack.empty() )
239+
{
240+
yamlFile << g_level1Next << "sourceCallStack:\n";
241+
for( size_t i = 0; i < errorMsg.m_sourceCallStack.size(); i++ )
242+
{
243+
yamlFile << ( errorMsg.isValidStackTrace() ?
244+
GEOS_FMT( "{}frame{}: {}\n", g_level3Start, i, errorMsg.m_sourceCallStack[i] ) :
245+
GEOS_FMT( "{}{}\n", g_level3Start, errorMsg.m_sourceCallStack[i] ) );
246+
}
247+
}
248+
249+
yamlFile << "\n";
250+
yamlFile.flush();
251+
errorMsg = ErrorMsg();
252+
GEOS_LOG_RANK( GEOS_FMT( "The error file {} was appended.", m_filename ) );
253+
}
254+
else
255+
{
256+
GEOS_LOG_RANK( GEOS_FMT( "Unable to open error file for writing.\n- Error file: {}\n- Error file enabled = {}.\n",
257+
m_filename, isOutputFileEnabled() ) );
258+
}
259+
}
260+
261+
} /* namespace geos */

0 commit comments

Comments
 (0)