MARLEY (Model of Argon Reaction Low Energy Yields) v2.0.0
A Monte Carlo event generator for tens-of-MeV neutrino interactions
Loading...
Searching...
No Matches
marley::Logger Class Reference

Simple singleton logging class. More...

#include <Logger.hh>

Classes

class  Message
 Temporary object used for forming logger messages. More...
 

Public Types

enum class  LogLevel {
  TRACE = MARLEY_LOGGER_LEVEL_TRACE , DEBUG = MARLEY_LOGGER_LEVEL_DEBUG , INFO = MARLEY_LOGGER_LEVEL_INFO , NOTICE = MARLEY_LOGGER_LEVEL_NOTICE ,
  WARN = MARLEY_LOGGER_LEVEL_WARN , ERROR = MARLEY_LOGGER_LEVEL_ERROR , FATAL = MARLEY_LOGGER_LEVEL_FATAL
}
 Defines the logging levels recognized by the marley::Logger. More...
 

Public Member Functions

 Logger ()
 Create the singleton Logger.
 
 Logger (const Logger &)=delete
 Deleted copy constructor.
 
 Logger (Logger &&)=delete
 Deleted move constructor.
 
LogLevel category_level (const std::string &category)
 Looks up the severity setting for the input category, including inheritance from the hierarchy.
 
void configure (const marley::JSON &json)
 Initialize the Logger using settings expressed as a JSON object.
 
bool has_stream (const std::ostream &stream) const
 Returns true if stream is already registered with the Logger, or false otherwise.
 
Message log (LogLevel lev, const std::string &category="")
 Prepare the Logger to receive a log message via the << stream operator.
 
Loggeroperator= (const Logger &)=delete
 Deleted copy assignment operator.
 
Loggeroperator= (Logger &&)=delete
 Deleted move assignment operator.
 
bool should_emit (const std::string &category, LogLevel lev)
 Returns whether the logger should emit a message for the given category and level.
 

Static Public Member Functions

static LoggerInstance ()
 Get the singleton instance of the Logger class.
 
static LogLevel string_to_loglevel (const std::string &str)
 

Detailed Description

Simple singleton logging class.

This class is based on code found here.

Definition at line 94 of file Logger.hh.

Member Enumeration Documentation

◆ LogLevel

enum class marley::Logger::LogLevel
strong

Defines the logging levels recognized by the marley::Logger.

Note that we tie the numerical values of the levels to the previously-defined pre-processor macros to ensure consistency. The levels appear in increasing numerical order (increasing severity).

Definition at line 102 of file Logger.hh.

102 {
103 TRACE = MARLEY_LOGGER_LEVEL_TRACE,
104 DEBUG = MARLEY_LOGGER_LEVEL_DEBUG,
105 INFO = MARLEY_LOGGER_LEVEL_INFO,
106 NOTICE = MARLEY_LOGGER_LEVEL_NOTICE,
107 WARN = MARLEY_LOGGER_LEVEL_WARN,
108 ERROR = MARLEY_LOGGER_LEVEL_ERROR,
109 FATAL = MARLEY_LOGGER_LEVEL_FATAL,
110 };

Constructor & Destructor Documentation

◆ Logger()

marley::Logger::Logger ( )

Create the singleton Logger.

Definition at line 213 of file Logger.cc.

213 {
214 // This is usually done with the FileManager, but we need to avoid logging
215 // messages in this constructor (to avoid recursive initialization). So we
216 // do it without the FileManager here.
217 char* mar = std::getenv( "MARLEY" );
218 if ( !mar ) throw marley::Error( "The MARLEY environment variable is not"
219 " set. Please set it (e.g., by sourcing the setup_marley.sh script) and"
220 " try again." );
221
222 // This works OK because the marley::JSON class does not use the Logger
223 std::string config_file_name = std::string( mar ) + "/data/config/logger.js";
224 auto json_config = marley::JSON::load_file( config_file_name );
225 this->configure( json_config );
226}
void configure(const marley::JSON &json)
Initialize the Logger using settings expressed as a JSON object.
Definition Logger.cc:88

References configure().

Referenced by Logger(), Logger(), Instance(), operator=(), and operator=().

Member Function Documentation

◆ category_level()

marley::Logger::LogLevel marley::Logger::category_level ( const std::string & category)

Looks up the severity setting for the input category, including inheritance from the hierarchy.

Definition at line 333 of file Logger.cc.

335{
336 // First check the cache. If we've already resolved the level for this
337 // category, then just use the result. This avoids unnecessary string
338 // splitting to check parent category settings.
339 auto iter = resolved_level_cache_.find( category );
340 if ( iter != resolved_level_cache_.end() ) return iter->second;
341
342 // Position of the delimiter used to mark category hierarchy separations
343 size_t delim_pos = std::string::npos;
344
345 // We need to resolve the severity level for a new category. Copy the
346 // input so that we can iteratively trim it to scan up the hierarchy.
347 std::string categ( category );
348 do {
349
350 // A value of delim_pos other than std::string::npos signals that we
351 // need to erase the rightmost category name so that we can look up
352 // the severity setting for the immediate parent category below.
353 if ( delim_pos != std::string::npos ) categ.erase( delim_pos );
354
355 // Check for a setting for the current category. The most specific setting
356 // wins, so cache the result and return immediately if one is found
357 const auto cit = category_map_.find( categ );
358 if ( cit != category_map_.cend() ) {
359 LogLevel resolved = cit->second;
360 resolved_level_cache_[ categ ] = resolved;
361 return resolved;
362 }
363
364 // Search for the last category delimiter in the current category string
365 delim_pos = categ.rfind( CATEG_DELIM_ );
366
367 // If one was not found, then exit the loop so we can fall back to the
368 // default severity level
369 } while( delim_pos != std::string::npos );
370
371 // A specific category setting was not found, so fall back to the default
372 // severity level
373 return default_level_;
374}
LogLevel
Defines the logging levels recognized by the marley::Logger.
Definition Logger.hh:102

Referenced by should_emit().

◆ configure()

void marley::Logger::configure ( const marley::JSON & json)

Initialize the Logger using settings expressed as a JSON object.

Definition at line 88 of file Logger.cc.

88 {
89
90 if ( config.has_key("out") ) {
91 auto out_array = config.at( "out" );
92 if ( !out_array.is_array() ) {
93 throw marley::Error( "JSON array expected for \"out\" key in"
94 " marley::Logger JSON configuration. Read \""
95 + out_array.dump_string() + '\"' );
96 }
97 auto streams = out_array.array_range();
98 for ( const auto& s : streams ) {
99
100 // Set defaults here that may be overwritten by the configuration
101 LogLevel min = LogLevel::TRACE;
102 LogLevel max = LogLevel::FATAL;
103
104 if ( s.has_key("min") ) {
105 auto jmin = s.at( "min" );
106 if ( !jmin.is_string() ) throw marley::Error("Invalid marley::Logger"
107 " minimum logging level specification " + jmin.dump_string() );
108 min = this->string_to_loglevel( jmin.to_string() );
109 }
110
111 if ( s.has_key("max") ) {
112 auto jmax = s.at( "max" );
113 if ( !jmax.is_string() ) throw marley::Error("Invalid marley::Logger"
114 " maximum logging level specification " + jmax.dump_string() );
115 max = this->string_to_loglevel( jmax.to_string() );
116 }
117
118 bool is_file = s.has_key( "file" );
119 bool is_stream = s.has_key( "stream" );
120 if ( is_file ) {
121 if ( is_stream ) {
122 throw marley::Error( "marley::Logger output stream definition uses"
123 "both the \"stream\" and \"file\" keys" );
124 }
125 auto fname = s.at( "file" );
126 if ( !fname.is_string() ) throw marley::Error("Invalid marley::Logger"
127 " output file specification " + fname.dump_string() );
128
129 // If the user specified a value for the "overwrite" key, use it
130 // to determine whether we should append to the file (false) or
131 // overwrite it (true). Otherwise, assume we want to append to it.
132 auto file_mode = std::ios::out;
133 if ( s.has_key("overwrite") ) {
134
135 auto ow = s.at( "overwrite" );
136
137 bool ok;
138 bool overwrite = ow.to_bool( ok );
139 if ( !ok ) throw marley::Error( "Invalid log file overwrite"
140 " setting \"" + ow.dump_string() + '\"' );
141
142 if ( overwrite ) file_mode |= std::ios::trunc;
143 else file_mode |= std::ios::app;
144 }
145 else file_mode |= std::ios::app;
146
147 auto outfile = std::make_shared< std::ofstream >( fname.to_string(),
148 file_mode );
149 if ( !outfile || (!outfile->good()) ) throw marley::Error( "Unable"
150 " to open the log file \"" + fname.to_string() + "\"" );
151
152 // Create the output stream for the file
153 this->add_stream( outfile, min, max );
154 }
155 else {
156 auto st = s.at( "stream" );
157 if ( !st.is_string() ) throw marley::Error("Invalid marley::Logger"
158 " output stream specification " + st.dump_string() );
159 auto stream_name = st.to_string();
160 if ( stream_name == "stdout" ) {
161 this->add_stream( std::cout, min, max );
162 }
163 else if ( stream_name == "stderr" ) {
164 this->add_stream( std::cerr, min, max );
165 }
166 else throw marley::Error( "Unrecognized stream name \"" + stream_name
167 + "\" encountered in marley::Logger::configure()" );
168 }
169
170 } // stream definitions
171 } // handling of "out" key
172
173 bool set_default_categ = false;
174 if ( !config.has_key("categories") ) {
175 // If the user has not configured any categories then globally set the
176 // logging level to INFO and move on
177 default_level_ = LogLevel::INFO;
178 return;
179 }
180
181 auto categ_spec = config.at( "categories" );
182 if ( !categ_spec.is_object() ) {
183 throw marley::Error( "marley::Logger categories must be specified as"
184 " a JSON object." );
185 }
186 auto categs = categ_spec.object_range();
187 for ( const auto& [ cat, lev ] : categs ) {
188 if ( !lev.is_string() ) {
189 throw marley::Error( "Invalid marley::Logger level specification \""
190 + lev.dump_string() + '\"' );
191 }
192 LogLevel ll = this->string_to_loglevel( lev.to_string() );
193 // The "default" category doesn't appear in the internal map. Instead,
194 // it has a dedicated class member to use as the ultimate fallback.
195 if ( cat == "default" ) {
196 set_default_categ = true;
197 default_level_ = ll;
198 }
199 else if ( cat.empty() ) {
200 throw marley::Error( "Empty name encountered in the category"
201 " configuration for marley::Logger" );
202 }
203 else category_map_[ cat ] = ll;
204 }
205
206 if ( !set_default_categ ) {
207 throw marley::Error( "Missing \"default\" logging level in the category"
208 " configuration for marley::Logger" );
209 }
210
211}

Referenced by Logger().

◆ has_stream()

bool marley::Logger::has_stream ( const std::ostream & stream) const

Returns true if stream is already registered with the Logger, or false otherwise.

Definition at line 233 of file Logger.cc.

233 {
234 auto stream = get_stream( &os );
235 if ( stream ) return true;
236 // A nullptr was returned, so the stream couldn't be found
237 else return false;
238}

◆ Instance()

marley::Logger & marley::Logger::Instance ( )
static

Get the singleton instance of the Logger class.

Definition at line 228 of file Logger.cc.

228 {
229 static Logger instance;
230 return instance;
231}
Logger()
Create the singleton Logger.
Definition Logger.cc:213

References Logger().

◆ log()

marley::Logger::Message marley::Logger::log ( LogLevel lev,
const std::string & category = "" )

Prepare the Logger to receive a log message via the << stream operator.

Parameters
levmarley::Logger::LogLevel of the incoming message
categoryNamed category of the incoming message

Definition at line 294 of file Logger.cc.

296{
297 std::vector< std::ostream* > active_streams;
298 // The current severity level determines whether the output Message will
299 // accept streamed content at all
300 if ( this->should_emit( category, lev) ) {
301 for( auto& s : streams_ ) {
302 // The Message will send the streamed content only to OutStreams whose
303 // severity levels are configured to accept it
304 if ( lev <= s.max_level_ && lev >= s.min_level_ ) {
305 // Store a pointer to the std::ostream object that will receive output
306 active_streams.push_back( s.stream_.get() );
307 }
308 }
309 }
310
311 // Returns a Message object to receive the output and relay it to the active
312 // OutStreams. Preprends a prefix based on the relevant logging level before
313 // accepting other output.
314 marley::Logger::Message msg( active_streams, &mutex_ );
315 msg << loglevel_to_str( lev );
316 return msg;
317}
bool should_emit(const std::string &category, LogLevel lev)
Returns whether the logger should emit a message for the given category and level.
Definition Logger.hh:337

References should_emit().

◆ should_emit()

bool marley::Logger::should_emit ( const std::string & category,
LogLevel lev )
inline

Returns whether the logger should emit a message for the given category and level.

Definition at line 337 of file Logger.hh.

339{
340 LogLevel cl = this->category_level( category );
341 return ( lev >= cl );
342}
LogLevel category_level(const std::string &category)
Looks up the severity setting for the input category, including inheritance from the hierarchy.
Definition Logger.cc:333

References category_level().

Referenced by log().

◆ string_to_loglevel()

marley::Logger::LogLevel marley::Logger::string_to_loglevel ( const std::string & str)
static

Definition at line 47 of file Logger.cc.

49{
50 LogLevel result = LogLevel::INFO;
51
52 // Convert the string to all-lowercase to achieve case-insensitive matching
53 std::string low = marley_utils::to_lowercase( str );
54 static const std::unordered_map< std::string, LogLevel > conversion_map = {
55 { "trace", LogLevel::TRACE },
56 { "debug", LogLevel::DEBUG },
57 { "info", LogLevel::INFO },
58 { "notice", LogLevel::NOTICE },
59 { "warn", LogLevel::WARN },
60 { "error", LogLevel::ERROR },
61 { "fatal", LogLevel::FATAL }
62 };
63 auto iter = conversion_map.find( low );
64 if ( iter != conversion_map.end() ) result = iter->second;
65 else throw marley::Error( "Unrecognized marley::Logger logging level \""
66 + str + '\"' );
67 return result;
68}

The documentation for this class was generated from the following files: