OBJ_DIR=obj
DEP_DIR=dep
-FS_SRC=$(wildcard ${SRC_DIR}/*.cpp) $(wildcard ${SRC_DIR}/io/*.cpp) $(wildcard ${SRC_DIR}/crypto/*.cpp) $(wildcard ${SRC_DIR}/db/*.cpp)
+FS_SRC=$(wildcard ${SRC_DIR}/*.cpp) $(wildcard ${SRC_DIR}/log/*.cpp) $(wildcard ${SRC_DIR}/io/*.cpp) $(wildcard ${SRC_DIR}/crypto/*.cpp) $(wildcard ${SRC_DIR}/db/*.cpp)
FS_BIN=$(wildcard ${SRC_DIR}/app/*.cpp)
FS_LIBS=$(wildcard lib/*/)
#!/bin/sh
-astyle --style=java --add-brackets --indent-col1-comments --break-blocks --pad-oper --pad-paren-in --unpad-paren --indent-namespaces --align-pointer=type --align-reference=type --convert-tabs --lineend=linux -r "*.cpp" "*.h"
+astyle --style=java --add-brackets --indent-col1-comments --break-blocks --pad-oper --pad-paren-in --unpad-paren --indent-namespaces --align-pointer=type --align-reference=type --convert-tabs --lineend=linux -r "*.cpp" "*.h" '*.hpp'
#include "crypto/simpleOpensslSigner.h"
#include "crypto/remoteSigner.h"
#include "crypto/sslUtil.h"
+#include "log/logger.hpp"
#include "util.h"
#include "io/bios.h"
#include "io/slipBio.h"
extern std::unordered_map<std::string, std::shared_ptr<CAConfig>> CAs;
void checkCRLs( std::shared_ptr<Signer> sign ) {
- std::cout << "Signing CRLs" << std::endl;
- for( auto x : CAs ) {
- std::cout << "Checking: " << x.first << std::endl;
+ logger::note( "Signing CRLs" );
+
+ for( auto& x : CAs ) {
+ logger::notef( "Checking: %s ...", x.first );
if( !x.second->crlNeedsResign() ) {
- std::cout << "Skipping Resigning CRL: " + x.second->name << std::endl;
+ logger::warnf( "Skipping Resigning CRL: %s ...", x.second->name );
continue;
}
- std::cout << "Resigning CRL: " + x.second->name << std::endl;
+ logger::notef( "Resigning CRL: %s ...", x.second->name );
try {
std::vector<std::string> serials;
std::pair<std::shared_ptr<CRL>, std::string> rev = sign->revoke( x.second, serials );
} catch( const char* c ) {
- std::cout << "Exception: " << c << std::endl;
+ logger::error( "Exception: ", c );
}
}
}
int main( int argc, const char* argv[] ) {
- ( void ) argc;
- ( void ) argv;
bool once = false;
- if( argc == 2 && std::string( "--once" ) == std::string( argv[1] ) ) {
+ if( argc == 2 && std::string( "--once" ) == argv[1] ) {
once = true;
}
#endif
if( parseConfig( path ) != 0 ) {
+ logger::fatal( "Error: Could not parse the configuration file." );
return -1;
}
if( serialPath == "" ) {
- std::cout << "Error: no serial device is given" << std::endl;
+ logger::fatal( "Error: no serial device is given!" );
return -1;
}
// todo set good log TODO FIXME
sign->setLog( std::shared_ptr<std::ostream>(
&std::cout,
- []( std::ostream * o ) {
+ []( std::ostream* o ) {
( void ) o;
} ) );
checkCRLs( sign );
std::shared_ptr<Job> job = jp->fetchJob();
if( !job ) {
- std::cout << "Nothing to work on" << std::endl;
+ logger::debug( "Nothing to work on." );
sleep( 5 );
continue;
}
std::shared_ptr<std::ofstream> logPtr = openLogfile( std::string( "logs/" ) + job->id + std::string( "_" ) + job->warning + std::string( ".log" ) );
- std::ofstream& log = *( logPtr.get() );
-
sign->setLog( logPtr );
- log << "TASK ID: " << job->id << std::endl;
- log << "TRY: " << job->warning << std::endl;
- log << "TARGET: " << job->target << std::endl;
- log << "TASK: " << job->task << std::endl << std::endl;
+ logger::note( "TASK ID: ", job->id );
+ logger::note( "TRY: ", job->warning );
+ logger::note( "TARGET: ", job->target );
+ logger::note( "TASK: ", job->task );
if( job->task == "sign" ) {
try {
std::shared_ptr<TBSCertificate> cert = jp->fetchTBSCert( job );
cert->wishFrom = job->from;
cert->wishTo = job->to;
- log << "INFO: message digest: " << cert->md << std::endl;
- log << "INFO: profile id: " << cert->profile << std::endl;
+ logger::note( "INFO: Message Digest: ", cert->md );
+ logger::note( "INFO: Profile ID: ", cert->profile );
for( auto& SAN : cert->SANs ) {
- log << "INFO: SAN " << SAN->type << ": " << SAN->content;
+ logger::notef( "INFO: SAN %s: %s", SAN->type, SAN->content );
}
for( auto& AVA : cert->AVAs ) {
- log << "INFO: AVA " << AVA->name << ": " << AVA->value;
+ logger::notef( "INFO: AVA %s: %s", AVA->name, AVA->value );
}
if( !cert ) {
- std::cout << "wasn't able to load CSR" << std::endl;
+ logger::error( "Unable to load CSR" );
jp->failJob( job );
continue;
}
- log << "FINE: Found the CSR at '" << cert->csr << "'" << std::endl;
+ logger::notef( "FINE: Found the CSR at '%s'", cert->csr );
cert->csr_content = readFile( keyDir + "/../" + cert->csr );
- log << "FINE: CSR is " << std::endl << cert->csr_content << std::endl;
+ logger::note( "FINE: CSR content:\n", cert->csr_content );
std::shared_ptr<SignedCertificate> res = sign->sign( cert );
if( !res ) {
- log << "ERROR: The signer failed. There was no certificate." << std::endl;
+ logger::error( "ERROR: The signer failed. No certificate was returned." );
jp->failJob( job );
continue;
}
- log << "FINE: CERTIFICATE LOG: " << res->log << std::endl;
- log << "FINE: CERTIFICATE:" << std::endl << res->certificate << std::endl;
+ logger::note( "FINE: CERTIFICATE LOG:\n", res->log );
+ logger::note( "FINE: CERTIFICATE:\n", res->certificate );
std::string fn = writeBackFile( job->target.c_str(), res->certificate, keyDir );
if( fn.empty() ) {
- log << "ERROR: Writeback of the certificate failed." << std::endl;
+ logger::error( "ERROR: Writeback of the certificate failed." );
jp->failJob( job );
continue;
}
res->crt_name = fn;
jp->writeBack( job, res ); //! \FIXME: Check return value
- log << "FINE: signing done." << std::endl;
+ logger::note( "FINE: signing done." );
if( DAEMON ) {
jp->finishJob( job );
continue;
} catch( const char* c ) {
- log << "ERROR: " << c << std::endl;
+ logger::error( "ERROR: ", c );
} catch( std::string& c ) {
- log << "ERROR: " << c << std::endl;
+ logger::error( "ERROR: ", c );
}
try {
jp->failJob( job );
} catch( const char* c ) {
- log << "ERROR: " << c << std::endl;
+ logger::error( "ERROR: ", c );
} catch( std::string& c ) {
- log << "ERROR: " << c << std::endl;
+ logger::error( "ERROR: ", c );
}
} else if( job->task == "revoke" ) {
try {
jp->writeBackRevocation( job, timeToString( time ) );
jp->finishJob( job );
} catch( const char* c ) {
- std::cout << "Exception: " << c << std::endl;
+ logger::error( "Exception: ", c );
} catch( const std::string& c ) {
- std::cout << "Exception: " << c << std::endl;
+ logger::error( "Exception: ", c );
}
} else {
- log << "Unknown job type" << job->task << std::endl;
+ logger::errorf( "Unknown job type (\"%s\")", job->task );
jp->failJob( job );
}
#include <iostream>
#include <fstream>
#include <streambuf>
+#include <stdexcept>
#include "db/database.h"
#include "db/mysql.h"
#include "io/bios.h"
#include "io/slipBio.h"
#include "io/recordHandler.h"
+#include "log/logger.hpp"
#include "util.h"
#include "config.h"
extern std::string serialPath;
-int main( int argc, const char* argv[] ) {
+int main( int argc, const char* argv[] ) try {
( void ) argc;
( void ) argv;
#endif
if( parseConfig( path ) != 0 ) {
+ logger::fatal( "Could not parse configuration file." );
return -1;
}
std::shared_ptr<int> ssl_lib = ssl_lib_ref;
if( serialPath == "" ) {
- std::cout << "Error: no serial device is given" << std::endl;
+ logger::fatal( "Error: No device for the serial connection was given." );
return -1;
}
//} catch( const std::exception &ch ) {
//std::cout << "Real exception: " << typeid(ch).name() << ", " << ch.what() << std::endl;
} catch( const std::string& ch ) {
- std::cout << "Exception: " << ch << std::endl;
+ logger::error( "Exception: ", ch );
} catch( char const* ch ) {
- std::cout << "Exception: " << ch << std::endl;
+ logger::error( "Exception: ", ch );
}
}
return -1;
+
+} catch( std::exception& e ) {
+ logger::fatalf( "Fatal Error: %s!\n", e.what() );
+ return -1;
+} catch( ... ) {
+ logger::fatal( "Fatal Error: Unknown Exception!\n" );
+ return -1;
}
#include "crypto/sslUtil.h"
+#include "log/logger.hpp"
+
std::string keyDir;
std::unordered_map<std::string, Profile> profiles;
std::unordered_map<std::string, std::shared_ptr<CAConfig>> CAs;
config.open( path );
if( !config.is_open() ) {
- std::cout << "Where is " << path << "?" << std::endl;
+ logger::notef( "Where is \"%s\"?", path );
throw "Config missing";
}
int splitter = line1.find( "=" );
if( splitter == -1 ) {
- std::cerr << "Ignoring malformed config line: " << line1 << std::endl;
+ logger::warn( "Ignoring malformed config line: ", line1 );
continue;
}
dp = opendir( "profiles" );
if( dp == NULL ) {
- std::cerr << "Profiles not found " << std::endl;
+ logger::error( "Profiles directory not found" );
return -1;
}
int splitter = profileName.find( "-" );
if( splitter == -1 ) {
- std::cerr << "Ignoring malformed profile: " << profileName << std::endl;
+ logger::warn( "Ignoring malformed profile: ", profileName );
continue;
}
std::string id = profileName.substr( 0, splitter );
if( profileName.substr( profileName.size() - 4 ) != ".cfg" ) {
- std::cerr << "Ignoring malformed profile: " << profileName << std::endl;
+ logger::warn( "Ignoring malformed profile: ", profileName );
continue;
}
std::string cas = map->at( "ca" );
- DIR *dir;
- struct dirent *ent;
- if ((dir = opendir ("ca")) != NULL) {
- while ((ent = readdir (dir)) != NULL) {
- std::string caName = std::string(ent->d_name);
- if( caName.find( cas ) != 0 ){
+ DIR* dir;
+ struct dirent* ent;
+
+ if( ( dir = opendir( "ca" ) ) != NULL ) {
+ while( ( ent = readdir( dir ) ) != NULL ) {
+ std::string caName = std::string( ent->d_name );
+
+ if( caName.find( cas ) != 0 ) {
continue;
}
}
prof.ca.push_back( CAs.at( caName ) );
- std::cout << "Adding CA: " << caName << std::endl;
+ logger::note( "Adding CA: ", caName );
}
- closedir (dir);
+
+ closedir( dir );
} else {
throw "Directory with CAConfigs not found";
}
profiles.emplace( profileName, prof );
- std::cout << "Profile: " << profileName << " up and running." << std::endl;
+ logger::notef( "Profile: \"%s\" up and running.", profileName );
}
( void ) closedir( dp );
-
- std::cout << profiles.size() << " profiles loaded." << std::endl;
+ logger::notef( "%s profiles loaded.", profiles.size() );
return 0;
}
int parseConfig( std::string path ) {
-
auto masterConf = parseConf( path );
keyDir = masterConf->at( "key.directory" );
serialPath = masterConf->at( "serialPath" );
if( keyDir == "" ) {
- std::cerr << "Missing config property key.directory" << std::endl;
+ logger::error( "Missing config property key.directory" );
return -1;
}
std::shared_ptr<char> serStr(
BN_bn2hex( ser.get() ),
- []( char *p ) {
- OPENSSL_free(p);
+ []( char* p ) {
+ OPENSSL_free( p );
} ); // OPENSSL_free is a macro...
res->serial = serStr ? std::string( serStr.get() ) : "";
#include "remoteSigner.h"
+
+#include "log/logger.hpp"
#include "util.h"
#include <iostream>
head.command_count++;
head.totalLength = data.size();
sendCommand( head, data, bio, log );
-
}
std::shared_ptr<SignedCertificate> RemoteSigner::sign( std::shared_ptr<TBSCertificate> cert ) {
} else if( cert->csr_type == "SPKAC" ) {
send( conn, head, RecordHeader::SignerCommand::SET_SPKAC, cert->csr_content );
} else {
- std::cout << "Unknown csr_type: " << cert->csr_type;
+ logger::error( "Unknown csr_type: ", cert->csr_type );
return std::shared_ptr<SignedCertificate>();
}
int length = conn->read( buffer.data(), buffer.size() );
if( length <= 0 ) {
- std::cout << "Error, no response data" << std::endl;
+ logger::error( "Error, no response data" );
result = std::shared_ptr<SignedCertificate>();
break;
}
break;
default:
- std::cout << "Invalid Message" << std::endl;
+ logger::error( "Invalid Message" );
break;
}
} catch( const char* msg ) {
- std::cout << msg << std::endl;
+ logger::error( msg );
return std::shared_ptr<SignedCertificate>();
}
}
}
if( !SSL_shutdown( ssl.get() ) && !SSL_shutdown( ssl.get() ) ) { // need to close the connection twice
- std::cout << "SSL shutdown failed" << std::endl;
+ logger::warn( "SSL shutdown failed" );
}
return result;
bool ok = crl->verify( ca );
if( ok ) {
- ( *log ) << "CRL verificated successfully" << std::endl;
+ logger::note( "CRL verificated successfully" );
writeFile( ca->path + std::string( "/ca.crl" ), crl->toString() );
} else {
- ( *log ) << "CRL is broken" << std::endl;
+ logger::warn( "CRL is broken, trying to recover" );
send( conn, head, RecordHeader::SignerCommand::GET_FULL_CRL, ca->name );
length = conn->read( buffer.data(), buffer.size() );
if( crl->verify( ca ) ) {
writeFile( ca->path + std::string( "/ca.crl" ), crl->toString() );
- ( *log ) << "CRL is now valid" << std::endl;
+ logger::note( "CRL is now valid again" );
} else {
- ( *log ) << "CRL is still broken... Please, help me" << std::endl;
+ logger::warn( "CRL is still broken... Please, help me" );
}
-
}
- ( *log ) << "CRL: " << std::endl << crl->toString() << std::endl;
+ logger::debug( "CRL:\n", crl->toString() );
if( !SSL_shutdown( ssl.get() ) && !SSL_shutdown( ssl.get() ) ) { // need to close the connection twice
- std::cout << "SSL shutdown failed" << std::endl;
+ logger::warn( "SSL shutdown failed" );
}
return std::pair<std::shared_ptr<CRL>, std::string>( crl, date );
#pragma once
+
#include <memory>
#include <openssl/ssl.h>
#include <openssl/engine.h>
#include <openssl/x509v3.h>
+#include "log/logger.hpp"
+
#include "X509.h"
#include "util.h"
#include "sslUtil.h"
std::shared_ptr<SignedCertificate> SimpleOpensslSigner::sign( std::shared_ptr<TBSCertificate> cert ) {
std::stringstream signlog;
- signlog << "FINE: profile is " << cert->profile << std::endl;
+ logger::note( "FINE: Profile name is: ", cert->profile );
Profile& prof = profiles.at( cert->profile );
- signlog << "FINE: Profile id is: " << prof.id << std::endl;
+ logger::note( "FINE: Profile ID is: ", prof.id );
std::shared_ptr<CAConfig> ca = prof.getCA();
if( !ca ) {
- signlog << "ERROR: Signing CA specified in profile could not be loaded." << std::endl;
+ logger::error( "ERROR: Signing CA specified in profile could not be loaded." );
throw "CA-key not found";
}
- signlog << "FINE: Key for Signing CA is correctly loaded." << std::endl;
+ logger::note( "FINE: Key for Signing CA is correctly loaded." );
- signlog << "INFO: Baseline Key Usage is: " << prof.ku << std::endl;
- signlog << "INFO: Extended Key Usage is: " << prof.eku << std::endl;
+ logger::note( "INFO: Baseline Key Usage is: ", prof.ku );
+ logger::note( "INFO: Extended Key Usage is: ", prof.eku );
- signlog << "FINE: Signing is wanted by: " << cert->wishFrom << std::endl;
- signlog << "FINE: Signing is wanted for: " << cert->wishTo << std::endl;
+ logger::note( "FINE: Signing is wanted by: ", cert->wishFrom );
+ logger::note( "FINE: Signing is wanted for: ", cert->wishTo );
std::shared_ptr<X509Req> req;
} else if( cert->csr_type == "CSR" ) {
req = X509Req::parseCSR( cert->csr_content );
} else {
- signlog << "ERROR: Unknown type of certification in request: " << cert->csr_type << std::endl;
- throw "Error, unknown REQ rype " + ( cert->csr_type );
+ logger::errorf( "ERROR: Unknown type (\"%s\") of certification in request.", cert->csr_type );
+ throw "Error, unknown REQ rype " + ( cert->csr_type ); //! \fixme: Pointer instead of string, please use proper exception classes
}
int i = req->verify();
} else if( i == 0 ) {
throw "Request contains a Signature that does not match ...";
} else {
- signlog << "FINE: Request contains valid self-signature." << std::endl;
+ logger::note( "FINE: Request contains valid self-signature." );
}
// Construct the Certificate
X509Cert c = X509Cert();
- signlog << "INFO: Populating RDN ..." << std::endl;
+ logger::note( "INFO: Populating RDN ..." );
+
for( std::shared_ptr<AVA> a : cert->AVAs ) {
- signlog << "INFO: Trying to add RDN: " << a->name << ": " << a->value << std::endl;
+ logger::notef( "INFO: Trying to add RDN: %s: %s", a->name, a->value );
if( a->name == "CN" ) {
c.addRDN( NID_commonName, a->value );
} else if( a->name == "OU" ) {
c.addRDN( NID_organizationalUnitName, a->value );
} else {
- signlog << "ERROR: Trying to add illegal RDN/AVA type: " << a->name << std::endl;
+ logger::error( "ERROR: Trying to add illegal RDN/AVA type: ", a->name );
throw "Unhandled/Illegal AVA type";
}
}
- signlog << "INFO: Populating Issuer ..." << std::endl;
+ logger::note( "INFO: Populating Issuer ..." );
c.setIssuerNameFrom( ca->ca );
- signlog << "INFO: Validating Public key for use in certificate" << std::endl;
- signlog << "INFO: - Checking generic key parameters" << std::endl;
- signlog << "FINE: ->Public Key parameters are okay" << std::endl;
+ logger::note( "INFO: Validating Public key for use in certificate" );
+ logger::note( "INFO: - Checking generic key parameters" );
+ logger::note( "FINE: ->Public Key parameters are okay" );
- signlog << "INFO: - Checking blacklists" << std::endl;
- signlog << "FINE: ->Does not appear on any blacklist" << std::endl;
+ logger::note( "INFO: - Checking blacklists" );
+ logger::note( "FINE: ->Does not appear on any blacklist" );
- signlog << "INFO: - Checking trivial factorization" << std::endl;
- signlog << "FINE: ->Trivial factorization not possible" << std::endl;
+ logger::note( "INFO: - Checking trivial factorization" );
+ logger::note( "FINE: ->Trivial factorization not possible" );
- signlog << "INFO: - Checking astrological signs" << std::endl;
- signlog << "FINE: ->The stars look good for this one" << std::endl;
- signlog << "FINE: Public key is fine for use in certificate" << std::endl;
+ logger::note( "INFO: - Checking astrological signs" );
+ logger::note( "FINE: ->The stars look good for this one" );
+ logger::note( "FINE: Public key is fine for use in certificate" );
- signlog << "INFO: Copying Public Key from Request ..." << std::endl;
+ logger::note( "INFO: Copying Public Key from Request ..." );
c.setPubkeyFrom( req );
- signlog << "FINE: Public Key successfully copied from Request." << std::endl;
+ logger::note( "FINE: Public Key successfully copied from Request." );
{
- signlog << "INFO: Determining Validity Period ..." << std::endl;
+ logger::note( "INFO: Determining Validity Period ..." );
std::time_t from, to;
std::time_t now = time( 0 );
std::pair<bool, std::time_t> parsed;
from = now;
}
- if( ((from - now) > /* 2 Weeks */ (2 * 7 * 24 * 60 * 60)) || ((now - from) >= 0) ) {
+ if( ( ( from - now ) > /* 2 Weeks */ ( 2 * 7 * 24 * 60 * 60 ) ) || ( ( now - from ) >= 0 ) ) {
from = now;
}
time_t limit = prof.maxValidity;
- if( (to - from > limit) || (to - from < 0) ) {
+ if( ( to - from > limit ) || ( to - from < 0 ) ) {
to = from + limit;
}
c.setTimes( from, to );
- signlog << "FINE: Setting validity period successful:" << std::endl;
+ logger::note( "FINE: Setting validity period successful:" );
{
struct tm* timeobj;
std::vector<char> timebuf;
- timeobj = gmtime(&from);
- timebuf.resize(128);
- timebuf.resize(std::strftime(const_cast<char *>(timebuf.data()), timebuf.size(), "%F %T %Z", timeobj));
- signlog << "FINE: - Valid not before: " << std::string(timebuf.cbegin(), timebuf.cend()) << std::endl;
+ timeobj = gmtime( &from );
+ timebuf.resize( 128 );
+ timebuf.resize( std::strftime( const_cast<char*>( timebuf.data() ), timebuf.size(), "%F %T %Z", timeobj ) );
+ logger::note( "FINE: - Valid not before: ", std::string( timebuf.cbegin(), timebuf.cend() ) );
- timeobj = gmtime(&to);
- timebuf.resize(128);
- timebuf.resize(std::strftime(const_cast<char *>(timebuf.data()), timebuf.size(), "%F %T %Z", timeobj));
- signlog << "FINE: - Valid not after: " << std::string(timebuf.cbegin(), timebuf.cend()) << std::endl;
+ timeobj = gmtime( &to );
+ timebuf.resize( 128 );
+ timebuf.resize( std::strftime( const_cast<char*>( timebuf.data() ), timebuf.size(), "%F %T %Z", timeobj ) );
+ logger::note( "FINE: - Valid not after: ", std::string( timebuf.cbegin(), timebuf.cend() ) );
}
}
- signlog << "INFO: Setting extensions:" << std::endl;
+ logger::note( "INFO: Setting extensions:" );
c.setExtensions( ca->ca, cert->SANs, prof );
- signlog << "FINE: Setting extensions successful." << std::endl;
+ logger::note( "FINE: Setting extensions successful." );
- signlog << "INFO: Generating next Serial Number ..." << std::endl;
+ logger::note( "INFO: Generating next Serial Number ..." );
std::shared_ptr<BIGNUM> ser;
std::string num;
std::tie( ser, num ) = nextSerial( prof, ca );
c.setSerialNumber( ser.get() );
- signlog << "FINE: Certificate Serial Number set to:" << num << std::endl;
+ logger::note( "FINE: Certificate Serial Number set to: ", num );
{
- signlog << "INFO: Trying to sign Certificate:" << std::endl;
+ logger::note( "INFO: Trying to sign Certificate:" );
std::shared_ptr<SignedCertificate> output = c.sign( ca->caKey, cert->md );
- signlog << "INFO: Writing certificate to local file." << std::endl;
+ logger::note( "INFO: Writing certificate to local file." );
std::string fn = writeBackFile( num, output->certificate, ca->path );
if( fn.empty() ) {
- signlog << "ERROR: failed to get filename for storage of signed certificate." << std::endl;
+ logger::error( "ERROR: failed to get filename for storage of signed certificate." );
throw "Storage location could not be determined";
}
- signlog << "FINE: Certificate signed successfully." << std::endl;
- signlog << "FINE: - Certificate written to: " << fn << std::endl;
+
+ logger::note( "FINE: Certificate signed successfully." );
+ logger::note( "FINE: - Certificate written to: ", fn );
output->ca_name = ca->name;
output->log = signlog.str();
return output;
}
-
}
std::pair<std::shared_ptr<CRL>, std::string> SimpleOpensslSigner::revoke( std::shared_ptr<CAConfig> ca, std::vector<std::string> serials ) {
#include <iostream>
#include "crypto/CRL.h"
+#include "log/logger.hpp"
std::shared_ptr<int> ssl_lib_ref(
new int( SSL_library_init() ),
return std::shared_ptr<X509>(
key,
- []( X509 * ref ) {
+ []( X509* ref ) {
X509_free( ref );
} );
}
std::shared_ptr<EVP_PKEY> loadPkeyFromFile( const std::string& filename ) {
- std::shared_ptr<FILE> f( fopen( filename.c_str(), "r" ), []( FILE * ptr ) {
- if( ptr ) {
- fclose( ptr );
- }
- } );
+ std::shared_ptr<FILE> f(
+ fopen( filename.c_str(), "r" ),
+ []( FILE* ptr ) {
+ if( ptr ) {
+ fclose( ptr );
+ }
+ } );
if( !f ) {
return std::shared_ptr<EVP_PKEY>();
return std::shared_ptr<EVP_PKEY>(
key,
- []( EVP_PKEY * ref ) {
+ []( EVP_PKEY* ref ) {
EVP_PKEY_free( ref );
} );
}
( void ) a;
( void ) b;
( void ) g;
+
std::cout << ( a == 0 ? "." : "+" ) << std::flush;
+
return 1;
}
//X509_print_ex(o, cert, XN_FLAG_COMPAT, X509_FLAG_COMPAT);
//BIO_free(o);
- std::cout << "Verification failed: " << preverify_ok << " because " << X509_STORE_CTX_get_error( ctx ) << std::endl;
+ logger::errorf( "Verification failed: %s because %s", preverify_ok, X509_STORE_CTX_get_error( ctx ) );
}
return preverify_ok;
static std::shared_ptr<DH> dh_param;
std::shared_ptr<SSL_CTX> generateSSLContext( bool server ) {
- std::shared_ptr<SSL_CTX> ctx = std::shared_ptr<SSL_CTX>( SSL_CTX_new( TLSv1_2_method() ), []( SSL_CTX * p ) {
- SSL_CTX_free( p );
- } );
+ std::shared_ptr<SSL_CTX> ctx = std::shared_ptr<SSL_CTX>(
+ SSL_CTX_new( TLSv1_2_method() ),
+ []( SSL_CTX* p ) {
+ SSL_CTX_free( p );
+ } );
if( !SSL_CTX_set_cipher_list( ctx.get(), "HIGH:+CAMELLIA256:!eNull:!aNULL:!ADH:!MD5:-RSA+AES+SHA1:!RC4:!DES:!3DES:!SEED:!EXP:!AES128:!CAMELLIA128" ) ) {
throw "Cannot set cipher list. Your source is broken.";
dh_param = std::shared_ptr<DH>( PEM_read_DHparams( paramfile.get(), NULL, NULL, NULL ), DH_free );
} else {
dh_param = std::shared_ptr<DH>( DH_new(), DH_free );
- std::cout << "Generating DH params" << std::endl;
+ logger::note( "Generating DH params" );
BN_GENCB cb;
cb.ver = 2;
cb.arg = 0;
std::shared_ptr<FILE> f( fopen( name.c_str(), "r+" ), fclose );
if( !f ) {
- std::cout << "Opening serial device failed" << std::endl;
+ logger::error( "Opening serial device failed." );
return std::shared_ptr<BIO>();
}
return std::shared_ptr<BIO>(
BIO_new_fd( fileno( f.get() ), 0 ),
[f]( BIO* b ) {
- BIO_free(b);
+ BIO_free( b );
} );
}
#include "db/database.h"
-class CAConfig {
-public:
+struct CAConfig {
std::string path;
std::string name;
std::shared_ptr<X509> ca;
std::shared_ptr<EVP_PKEY> caKey;
std::shared_ptr<ASN1_TIME> notBefore;
+
CAConfig( const std::string& name );
+
bool crlNeedsResign();
};
std::shared_ptr<SSL_CTX> generateSSLContext( bool server );
std::shared_ptr<BIO> openSerial( const std::string& name );
std::string timeToString( std::shared_ptr<ASN1_TIME> time );
+
void extractTimes( std::shared_ptr<X509> source, std::shared_ptr<SignedCertificate> cert );
-#include "database.h"
+#include "db/database.h"
struct TBSCertificate {
std::string md;
std::string profile;
+
/**
* CSR path
*/
std::string wishTo;
};
-
struct SignedCertificate {
std::string certificate;
std::string serial;
class JobProvider {
public:
+ virtual ~JobProvider() = default;
virtual std::shared_ptr<Job> fetchJob() = 0;
virtual void finishJob( std::shared_ptr<Job> job ) = 0;
virtual void failJob( std::shared_ptr<Job> job ) = 0;
std::shared_ptr<int> MySQLJobProvider::lib_ref(
//Initializer: Store the return code as a pointer to an integer
new int( mysql_library_init( 0, NULL, NULL ) ),
+
//Finalizer: Check the pointer and free resources
[]( int* ref ) {
if( !ref ) {
connect( server, user, password, database );
}
-MySQLJobProvider::~MySQLJobProvider() {
- disconnect();
-}
-
bool MySQLJobProvider::connect( const std::string& server, const std::string& user, const std::string& password, const std::string& database ) {
- if( conn ) {
- if( !disconnect() ) {
- return false;
- }
-
- conn.reset();
- }
-
+ disconnect();
conn = _connect( server, user, password, database );
return !!conn;
MYSQL* tmp( mysql_init( NULL ) );
if( !tmp ) {
- return std::shared_ptr<MYSQL>();
+ return nullptr;
}
tmp = mysql_real_connect( tmp, server.c_str(), user.c_str(), password.c_str(), database.c_str(), 3306, NULL, CLIENT_COMPRESS );
if( !tmp ) {
- return std::shared_ptr<MYSQL>();
+ return nullptr;
}
auto l = lib_ref;
std::tie( err, res ) = query( q );
if( err ) {
- return std::shared_ptr<Job>();
+ return nullptr;
}
unsigned int num = mysql_num_fields( res.get() );
MYSQL_ROW row = mysql_fetch_row( res.get() );
if( !row ) {
- return std::shared_ptr<Job>();
+ return nullptr;
}
- std::shared_ptr<Job> job( new Job() );
+ auto job = std::make_shared<Job>();
unsigned long* l = mysql_fetch_lengths( res.get() );
if( !l ) {
- return std::shared_ptr<Job>();
+ return nullptr;
}
job->id = std::string( row[0], row[0] + l[0] );
if( query( q ).first ) {
throw "No database entry found.";
}
-
}
void MySQLJobProvider::failJob( std::shared_ptr<Job> job ) {
}
std::shared_ptr<TBSCertificate> MySQLJobProvider::fetchTBSCert( std::shared_ptr<Job> job ) {
- std::shared_ptr<TBSCertificate> cert = std::shared_ptr<TBSCertificate>( new TBSCertificate() );
+ auto cert = std::make_shared<TBSCertificate>();
std::string q = "SELECT md, profile, csr_name, csr_type, keyname FROM certs INNER JOIN profiles ON profiles.id = certs.profile WHERE certs.id='" + this->escape_string( job->target ) + "'";
int err = 0;
std::tie( err, res ) = query( q );
if( err ) {
- return std::shared_ptr<TBSCertificate>();
+ return nullptr;
}
MYSQL_ROW row = mysql_fetch_row( res.get() );
if( !row ) {
- return std::shared_ptr<TBSCertificate>();
+ return nullptr;
}
unsigned long* l = mysql_fetch_lengths( res.get() );
if( !l ) {
- return std::shared_ptr<TBSCertificate>();
+ return nullptr;
}
std::string profileName = std::string( row[4], row[4] + l[4] );
if( err ) {
std::cout << mysql_error( this->conn.get() );
- return std::shared_ptr<TBSCertificate>();
+ return nullptr;
}
std::cout << "Fetching SANs" << std::endl;
unsigned long* l = mysql_fetch_lengths( res.get() );
if( !l ) {
- return std::shared_ptr<TBSCertificate>();
+ return nullptr;
}
std::shared_ptr<SAN> nSAN = std::shared_ptr<SAN>( new SAN() );
if( err ) {
std::cout << mysql_error( this->conn.get() );
- return std::shared_ptr<TBSCertificate>();
+ return nullptr;
}
unsigned long* l = mysql_fetch_lengths( res.get() );
if( !l ) {
- return std::shared_ptr<TBSCertificate>();
+ return nullptr;
}
std::shared_ptr<AVA> nAVA = std::shared_ptr<AVA>( new AVA() );
public:
MySQLJobProvider( const std::string& server, const std::string& user, const std::string& password, const std::string& database );
- ~MySQLJobProvider();
public:
bool connect( const std::string& server, const std::string& user, const std::string& password, const std::string& database );
#include <memory>
#include <sstream>
-#include "bios.h"
-#include "opensslBIO.h"
+#include "io/bios.h"
+#include "io/opensslBIO.h"
+#include "log/logger.hpp"
std::string toHexAndChecksum( const std::string& src ) {
char checksum = 0;
std::string res = toHexAndChecksum( s );
if( log ) {
- ( *log.get() ) << "FINE: RECORD output: " << res << std::endl;
+ logger::debug( "FINE: RECORD output: ", res );
}
bio->write( res.data(), res.size() );
std::string parseCommand( RecordHeader& head, const std::string& input, std::shared_ptr<std::ostream> log ) {
if( log ) {
- ( *log.get() ) << "FINE: RECORD input: " << input << std::endl;
+ logger::debug( "FINE: RECORD input: ", input );
}
int32_t dlen = ( input.size() - 2 ) / 2;
bool error = false;
std::string str( std::max( dlen, RECORD_HEADER_SIZE ), 0 );
- str.append( 0, dlen );
+ str.append( dlen, '\0' );
for( int i = 0; i < dlen; i++ ) {
int32_t digit;
#include "db/database.h"
#include "crypto/remoteSigner.h"
#include "crypto/sslUtil.h"
-
#include "crypto/simpleOpensslSigner.h"
+#include "log/logger.hpp"
+
extern std::vector<Profile> profiles;
extern std::unordered_map<std::string, std::shared_ptr<CAConfig>> CAs;
int res = io->read( buffer.data(), buffer.capacity() );
if( res <= 0 ) {
- ( *log ) << "Stream error, resetting SSL" << std::endl;
+ logger::error( "Stream error, resetting SSL" );
parent->reset();
return;
}
execute( head, payload );
} catch( const char* msg ) {
if( log ) {
- ( *log ) << "ERROR: " << msg << std::endl;
+ logger::error( "ERROR: ", msg );
}
parent->reset();
case RecordHeader::SignerCommand::SET_CSR:
tbs->csr_content = data;
tbs->csr_type = "CSR";
- ( *log ) << "INFO: CSR read: " << tbs->csr_content << std::endl;
+ logger::note( "INFO: CSR read:\n", tbs->csr_content );
break;
case RecordHeader::SignerCommand::SET_SPKAC:
tbs->csr_content = data;
tbs->csr_type = "SPKAC";
- ( *log ) << "INFO: SPKAC read: " << tbs->csr_content << std::endl;
+ logger::note( "INFO: SPKAC read:\n", tbs->csr_content );
break;
case RecordHeader::SignerCommand::SET_SIGNATURE_TYPE:
tbs->wishTo = data;
break;
- case RecordHeader::SignerCommand::ADD_SAN: {
- size_t pos = data.find( "," );
-
- if( pos == std::string::npos ) {
- // error
- } else {
- std::shared_ptr<SAN> san( new SAN() );
- san->type = data.substr( 0, pos );
- san->content = data.substr( pos + 1 );
- tbs->SANs.push_back( san );
+ case RecordHeader::SignerCommand::ADD_SAN:
+ {
+ size_t pos = data.find( "," );
+
+ if( pos == std::string::npos ) {
+ // error
+ } else {
+ std::shared_ptr<SAN> san( new SAN() );
+ san->type = data.substr( 0, pos );
+ san->content = data.substr( pos + 1 );
+ tbs->SANs.push_back( san );
+ }
}
- }
- break;
-
- case RecordHeader::SignerCommand::ADD_AVA: {
- size_t pos = data.find( "," );
-
- if( pos == std::string::npos ) {
- // error
- } else {
- std::shared_ptr<AVA> ava( new AVA() );
- ava->name = data.substr( 0, pos );
- ava->value = data.substr( pos + 1 );
- tbs->AVAs.push_back( ava );
+ break;
+
+ case RecordHeader::SignerCommand::ADD_AVA:
+ {
+ size_t pos = data.find( "," );
+
+ if( pos == std::string::npos ) {
+ // error
+ } else {
+ std::shared_ptr<AVA> ava( new AVA() );
+ ava->name = data.substr( 0, pos );
+ ava->value = data.substr( pos + 1 );
+ tbs->AVAs.push_back( ava );
+ }
}
- }
- break;
+ break;
case RecordHeader::SignerCommand::ADD_PROOF_LINE:
break;
case RecordHeader::SignerCommand::SIGN:
result = signer->sign( tbs );
- ( *log ) << "INFO: signlog: " << result->log << std::endl;
- ( *log ) << "INFO: res: " << result->certificate << std::endl;
+ logger::note( "INFO: signlog:\n", result->log );
+ logger::note( "INFO: res:\n", result->certificate );
respondCommand( RecordHeader::SignerResult::SAVE_LOG, result->log );
break;
}
if( !SSL_shutdown( ssl.get() ) && !SSL_shutdown( ssl.get() ) ) {
- ( *log ) << "ERROR: SSL close failed" << std::endl;
+ logger::warn( "ERROR: SSL shutdown failed." );
}
+
parent->reset(); // Connection ended
break;
serials.push_back( data );
break;
- case RecordHeader::SignerCommand::REVOKE: {
- std::string ca = data;
- auto reqCA = CAs.at( ca );
- ( *log ) << "CA found" << std::endl;
- std::shared_ptr<CRL> crl;
- std::string date;
- std::tie<std::shared_ptr<CRL>, std::string>( crl, date ) = signer->revoke( reqCA, serials );
-
- respondCommand( RecordHeader::SignerResult::REVOKED, date + crl->getSignature() );
+ case RecordHeader::SignerCommand::REVOKE:
+ {
+ std::string ca = data;
+ auto reqCA = CAs.at( ca );
+ logger::note( "CA found" );
+ std::shared_ptr<CRL> crl;
+ std::string date;
+ std::tie<std::shared_ptr<CRL>, std::string>( crl, date ) = signer->revoke( reqCA, serials );
+ respondCommand( RecordHeader::SignerResult::REVOKED, date + crl->getSignature() );
+ }
break;
- }
- case RecordHeader::SignerCommand::GET_FULL_CRL: {
- auto ca = CAs.at( data );
- CRL c( ca->path + "/ca.crl" );
- respondCommand( RecordHeader::SignerResult::FULL_CRL, c.toString() );
+ case RecordHeader::SignerCommand::GET_FULL_CRL:
+ {
+ auto ca = CAs.at( data );
+ CRL c( ca->path + "/ca.crl" );
+ respondCommand( RecordHeader::SignerResult::FULL_CRL, c.toString() );
- if( !SSL_shutdown( ssl.get() ) && !SSL_shutdown( ssl.get() ) ) {
- ( *log ) << "ERROR: SSL close failed" << std::endl;
+ if( !SSL_shutdown( ssl.get() ) && !SSL_shutdown( ssl.get() ) ) {
+ logger::error( "ERROR: SSL shutdown failed." );
+ }
+
+ parent->reset(); // Connection ended
}
- parent->reset(); // Connection ended
break;
- }
default:
throw "Unimplemented";
}
};
-DefaultRecordHandler::DefaultRecordHandler( std::shared_ptr<Signer> signer, std::shared_ptr<BIO> bio )
- : bio( bio ), ctx( generateSSLContext( true ) ), signer( signer ), currentSession() {
+DefaultRecordHandler::DefaultRecordHandler( std::shared_ptr<Signer> signer, std::shared_ptr<BIO> bio ) :
+ bio( bio ), ctx( generateSSLContext( true ) ), signer( signer ), currentSession() {
}
void DefaultRecordHandler::reset() {
void DefaultRecordHandler::handle() {
if( !currentSession ) {
- std::cout << "session allocated" << std::endl;
+ logger::note( "New session allocated." );
currentSession = std::shared_ptr<RecordHandlerSession>( new RecordHandlerSession( this, signer, ctx, bio ) );
}
#include <iostream>
+#include "log/logger.hpp"
+
#define BUFFER_SIZE 8192
#define SLIP_ESCAPE_CHAR ( (char) 0xDB)
int SlipBIO::write( const char* buf, int num ) {
#ifdef SLIP_IO_DEBUG
- std::cout << "Out: " << toHex( buf, num ) << std::endl;
+ logger::debug( "Out: " << toHex( buf, num ) );
#endif
int badOnes = 0;
}
#ifdef SLIP_IO_DEBUG
- std::cout << "in: " << toHex( buf, len ) << std::endl;
+ logger::debug( "in: " << toHex( buf, len ) );
#endif
return len;
decodePos = 0;
decodeTarget = 0;
rawPos = 0;
- std::cout << "resetting SLIP" << std::endl;
+ logger::note( "Resetting SLIP layer" );
return 0;
}
#include <memory>
#include <vector>
-#include "bios.h"
+#include "io/bios.h"
class SlipBIO : public OpensslBIO {
private:
--- /dev/null
+#include "log/format.hpp"
+
+#include <algorithm>
+#include <cctype>
+#include <tuple>
+
+namespace logger {
+
+ namespace format {
+
+ inline namespace literals {
+
+ format_data operator"" _fmt( const char* it, std::size_t len ) {
+ const auto end = it + len;
+ auto retval = format_data {};
+
+ if( it == end ) {
+ return retval;
+ }
+
+ if( *it == '0' or !std::isalnum( *it ) ) {
+ retval.fill = *it;
+ ++it;
+ }
+
+ if( it == end ) {
+ return retval;
+ }
+
+ if( std::isdigit( *it ) ) {
+ const auto w_end = std::find_if_not( it, end,
+ []( char c ) {
+ return std::isdigit( c );
+ } );
+ retval.width = std::stoul( std::string{it, w_end} );
+ it = w_end;
+ }
+
+ if( it == end ) {
+ return retval;
+ }
+
+ switch( *it ) {
+ case 's':
+ break;
+
+ case 'd':
+ retval.base = 10;
+ break;
+
+ case 'x':
+ retval.base = 16;
+ break;
+
+ case 'o':
+ retval.base = 8;
+ break;
+
+ case 'l':
+ retval.align_right = false;
+ break;
+
+ case 'r':
+ retval.align_right = true;
+ break;
+
+ default:
+ throw std::invalid_argument{"invalid format_data-string"};
+ }
+
+ ++it;
+
+ if( it != end ) {
+ throw std::invalid_argument{"invalid format_data-string"};
+ }
+
+ return retval;
+ }
+
+ } // inline namespace literals
+
+ } // namespace format
+
+ namespace conv {
+
+ std::string to_string( const format::formated_string& arg ) {
+ if( arg.value.size() >= arg.width ) {
+ return arg.value;
+ }
+
+ auto str = std::string {};
+ str.reserve( arg.width );
+
+ if( arg.align_right ) {
+ str.append( arg.width - arg.value.size(), arg.fill );
+ str.append( arg.value );
+ } else {
+ str.append( arg.value );
+ str.append( arg.width - arg.value.size(), arg.fill );
+ }
+
+ return str;
+ }
+
+ } // namespace conv
+
+} // namespace logger
--- /dev/null
+#pragma once
+
+#include <string>
+#include <sstream>
+#include <iomanip>
+#include <initializer_list>
+#include <tuple>
+
+namespace logger {
+
+ namespace format {
+
+ template <typename Integer>
+ struct formated_integer;
+ struct formated_string;
+
+ struct width_t {
+ explicit width_t( unsigned v ): value{v} {}
+ unsigned value = 0u;
+ };
+ struct base_t {
+ explicit base_t( unsigned v ): value{v} {}
+ unsigned value = 10u;
+ };
+ struct fill_t {
+ explicit fill_t( char v ): value{v} {}
+ char value = ' ';
+ };
+ enum class align_t {
+ left, right
+ };
+
+ inline namespace literals {
+ inline width_t operator"" _w( unsigned long long value ) {
+ return width_t{static_cast<unsigned>( value )};
+ }
+ inline base_t operator"" _b( unsigned long long value ) {
+ return base_t{static_cast<unsigned>( value )};
+ }
+ inline fill_t operator"" _f( char c ) {
+ return fill_t{c};
+ }
+ }
+
+ struct format_data {
+ unsigned width = 0;
+ std::uint8_t base = 10;
+ char fill = ' ';
+ bool align_right = false;
+
+ void set( width_t w ) {
+ width = w.value;
+ }
+ void set( base_t b ) {
+ base = b.value;
+ }
+ void set( fill_t f ) {
+ fill = f.value;
+ }
+ void set( align_t a ) {
+ align_right = ( a == align_t::right );
+ }
+
+ formated_string operator()( const std::string& str ) const;
+
+ template <typename Integer,
+ typename = typename std::enable_if<std::is_integral<Integer>::value>::type>
+ formated_integer<Integer> operator()( Integer i ) const;
+ };
+
+ template <typename Integer>
+ struct formated_integer : public format_data {
+ formated_integer( Integer i, format_data f ) : format_data( f ), value {i} {}
+ Integer value;
+ };
+
+ struct formated_string : public format_data {
+ formated_string( const std::string& s, format_data f ) :
+ format_data( f ), value( std::move( s ) ) {}
+
+ const std::string& value;
+ };
+
+ inline formated_string format_data::operator()( const std::string& str ) const {
+ return {str, *this};
+ }
+
+ template <typename Integer, typename>
+ inline formated_integer<Integer> format_data::operator()( Integer i ) const {
+ return {i, *this};
+ }
+
+ template <typename... Args>
+ formated_string fmt( const std::string& str, const Args& ... args ) {
+ auto format = format_data{};
+ std::ignore = std::initializer_list<int>{( format.set( args ), 0 )...};
+ return format( str );
+ }
+
+ template <typename Integer, typename... Args>
+ formated_integer<Integer> fmt( const Integer i, const Args& ... args ) {
+ auto format = format_data{};
+ std::ignore = std::initializer_list<int>{( format.set( args ), 0 )...};
+ return format( i );
+ }
+
+ inline namespace literals {
+ format_data operator"" _fmt( const char*, std::size_t );
+ }
+
+ } // namespace format
+
+ namespace conv {
+
+ template <typename Integer>
+ inline std::string to_string( const format::formated_integer<Integer>& arg ) {
+ std::ostringstream stream;
+ stream <<
+ std::setbase( arg.base ) <<
+ std::setw( arg.width ) <<
+ std::setfill( arg.fill ) <<
+ arg.value;
+ return stream.str();
+ }
+
+ std::string to_string( const format::formated_string& arg );
+
+ } // namespace conf
+
+} // namespace logger
--- /dev/null
+#include "log/logger.hpp"
+
+#include <cassert>
+#include <iterator>
+#include <algorithm>
+#include <chrono>
+#include <cstring>
+#include <ostream>
+#include <iterator>
+
+namespace logger {
+
+ namespace impl {
+
+ /**
+ * Manages the standard-logger and the logger-stack.
+ *
+ * CAREFULL: THIS FUNCTION CONTAINS GLOBAL STATE!
+ */
+ std::vector<logger_set*>& logger_stack() {
+ static auto stack = std::vector<logger_set*> {};
+ // To avoid infinite recursion, the base-logger must
+ // not auto-register but be added manually
+ static auto std_logger = logger_set {{std::cout}, auto_register::off};
+ // in order to avoid use-after-free bugs, the logger must be created after
+ // the stack, to avoid that it's destructor tries to access
+ // parts of the destroyed stack
+
+ static auto dummy = [&] {
+ stack.push_back( &std_logger );
+ return 0;
+ }();
+
+ ( void ) dummy;
+ return stack;
+ }
+
+ void reassign_stack_pointer( logger_set*& ptr ) {
+ const auto old_ptr = ptr;
+
+ if( ptr ) {
+ ptr->m_stackpointer = &ptr;
+ }
+
+ ( void ) old_ptr;
+ assert( ptr == old_ptr );
+ }
+
+ void register_logger( logger_set& set ) {
+ auto& stack = logger_stack();
+
+ // we need to reassign everything if the vector reallocated:
+ const auto old_capacity = stack.capacity();
+ stack.push_back( &set );
+
+ if( stack.capacity() == old_capacity ) {
+ reassign_stack_pointer( stack.back() );
+ } else {
+ for( auto& ptr : stack ) {
+ reassign_stack_pointer( ptr );
+ }
+ }
+ }
+
+ /**
+ * Pops loggers from the stack until the last one is not a nullptr
+ */
+ void pop_loggers() {
+ auto& stack = logger_stack();
+
+ while( !stack.empty() and stack.back() == nullptr ) {
+ stack.pop_back();
+ }
+
+ assert( stack.empty() or stack.back() != nullptr );
+ }
+
+ logger_set& active_logger() {
+ const auto result = logger_stack().back();
+ assert( result != nullptr );
+ return *result;
+ }
+
+ } // namespace impl
+
+ logger_set::logger_set( std::initializer_list<log_target> lst, auto_register r ):
+ m_loggers{lst}, m_min_level{default_level} {
+ if( lst.size() > 0 ) {
+ m_min_level = std::min_element( lst.begin(), lst.end(),
+ []( const log_target& l, const log_target& r ) {
+ return l.min_level < r.min_level;
+ } )->min_level;
+ }
+
+ if( r == auto_register::on ) {
+ impl::register_logger( *this );
+ }
+ }
+
+ logger_set::~logger_set() {
+ if( m_stackpointer ) {
+ *m_stackpointer = nullptr;
+ impl::pop_loggers();
+ }
+ }
+
+ logger_set::logger_set( logger_set&& other ) noexcept :
+ m_loggers{std::move( other.m_loggers )}, m_stackpointer{other.m_stackpointer}, m_min_level{other.m_min_level} {
+ other.m_stackpointer = nullptr;
+
+ if( m_stackpointer ) {
+ *m_stackpointer = this;
+ }
+ }
+
+ logger_set& logger_set::operator=( logger_set && other ) noexcept {
+ if( m_stackpointer ) {
+ *m_stackpointer = nullptr;
+ impl::pop_loggers();
+ }
+
+ m_loggers = std::move( other.m_loggers );
+ m_stackpointer = other.m_stackpointer;
+ m_min_level = other.m_min_level;
+ other.m_stackpointer = nullptr;
+
+ if( m_stackpointer ) {
+ *m_stackpointer = this;
+ }
+
+ return *this;
+ }
+
+ void logger_set::log_impl( level l, const std::string& msg ) {
+ for( auto& logger : m_loggers ) {
+ if( l >= logger.min_level ) {
+ *logger.stream << msg << std::flush;
+ }
+ }
+ }
+
+ logger_set current_logger_extended( std::initializer_list<log_target> further_targets ) {
+ auto& active = impl::active_logger();
+ auto returnvalue = logger_set{further_targets};
+ returnvalue.m_loggers.insert( returnvalue.m_loggers.end(), active.m_loggers.begin(), active.m_loggers.end() );
+ returnvalue.m_min_level = active.m_min_level;
+ return returnvalue;
+ }
+
+ namespace {
+
+ std::string make_prefix( level l ) {
+ auto prefix = std::string {};
+
+ switch( l ) {
+ case level::debug:
+ prefix = "[debug][";
+ break;
+
+ case level::note:
+ prefix = "[note ][";
+ break;
+
+ case level::warn:
+ prefix = "[warn ][";
+ break;
+
+ case level::error:
+ prefix = "[error][";
+ break;
+
+ case level::fatal:
+ prefix = "[fatal][";
+ break;
+ }
+
+ using clock = std::chrono::system_clock;
+ const auto now = clock::to_time_t( clock::now() );
+ // ctime appends a newline, we don't want that here:
+ auto time_str = std::ctime( &now );
+ prefix.append( time_str, time_str + std::strlen( time_str ) - 1 );
+ prefix += "]: ";
+ return prefix;
+ }
+
+ } // anonymous namespace
+
+ namespace impl {
+
+ std::string replace_newlines( const std::string& str, std::size_t length ) {
+ auto returnstring = std::string {};
+ auto it = str.begin();
+ const auto end = str.end();
+ auto nl_it = it;
+
+ while( ( nl_it = std::find( it, end, '\n' ) ) != end ) {
+ ++nl_it;
+ returnstring.append( it, nl_it );
+ returnstring.append( length, ' ' );
+ it = nl_it;
+ }
+
+ returnstring.append( it, end );
+ return returnstring;
+ }
+
+
+ std::string concat_msg( level l, const std::vector<std::string>& args ) {
+ auto msg = make_prefix( l );
+ const auto prefix_length = msg.length();
+
+ for( const auto& arg : args ) {
+ msg += replace_newlines( arg, prefix_length );
+ }
+
+ msg += '\n';
+ return msg;
+ }
+
+ std::string format_msg( level l, const std::string& format, std::vector<std::string> args ) {
+ const auto prefix = make_prefix( l );
+ const auto length = prefix.length();
+ const auto fmt = replace_newlines( format, length );
+ std::transform( args.begin(), args.end(), args.begin(),
+ [length]( const std::string & str ) {
+ return replace_newlines( str, length );
+ } );
+
+ auto msg = prefix;
+ auto arg_index = std::size_t {0};
+ auto it = fmt.begin();
+ const auto end = fmt.end();
+
+ while( it != end ) {
+ auto pos = std::find( it, end, '%' );
+ msg.append( it, pos );
+
+ if( pos == end ) {
+ break;
+ }
+
+ ++pos;
+
+ if( pos == end ) {
+ throw std::invalid_argument {"Invalid formatstring (ends on single '%')"};
+ }
+
+ switch( *pos ) {
+ case '%':
+ msg.push_back( '%' );
+ break;
+
+ case 's':
+ if( arg_index >= args.size() ) {
+ throw std::invalid_argument {"Invalid formatstring (not enough arguments)"};
+ }
+
+ msg.append( args[arg_index++] );
+ break;
+
+ default:
+ throw std::invalid_argument {"Invalid formatstring (unknown format-character)"};
+ }
+
+ it = std::next( pos );
+ }
+
+ msg.push_back( '\n' );
+ return msg;
+ }
+
+ } // namespace impl
+
+} // namespace logger
--- /dev/null
+#pragma once
+
+#include <algorithm>
+#include <iomanip>
+#include <iostream>
+#include <string>
+#include <sstream>
+#include <vector>
+#include <fstream>
+#include <stdexcept>
+
+namespace logger {
+
+ /**
+ * conv::to_string will be used to convert whatever argument is send
+ * to the logger to a string. If another type shall be supported,
+ * just add the appropriate overload and you can start using it right
+ * away. The default conversion will use a stringstream.
+ */
+ namespace conv {
+ template <typename T>
+ inline std::string to_string( const T& arg ) {
+ std::ostringstream stream;
+ stream << arg;
+ return stream.str();
+ }
+ inline std::string to_string( std::string&& arg ) {
+ return arg;
+ }
+ inline std::string to_string( const std::string& arg ) {
+ return arg;
+ }
+ inline std::string to_string( const char* arg ) {
+ return arg;
+ }
+
+ } // namespace conv
+
+ enum class level {
+ debug, note, warn, error, fatal
+ };
+
+ const auto default_level = level::note;
+
+ struct log_target {
+ log_target( std::ostream& stream, level min_level = default_level ):
+ stream {&stream}, min_level {min_level} {}
+ log_target( std::ofstream& stream, level min_level = default_level ):
+ stream {&stream}, min_level {min_level} {
+ if( !stream.is_open() ) {
+ throw std::runtime_error {"logfile not open"};
+ }
+ }
+ std::ostream* stream;
+ level min_level;
+ };
+
+ class logger_set;
+ namespace impl {
+ void reassign_stack_pointer( logger_set*& ptr );
+ }
+
+ /**
+ * Provides controll over wether a logger should automatically register itself
+ */
+ enum class auto_register {
+ on, off
+ };
+
+ class logger_set {
+ public:
+ logger_set( std::initializer_list<log_target> lst, auto_register = auto_register::on );
+
+ logger_set( logger_set&& ) noexcept;
+ logger_set& operator=( logger_set && ) noexcept;
+ ~logger_set();
+
+ template<typename... Args>
+ void log( level l, Args&& ... args );
+ template<typename... Args>
+ void logf( level l, const std::string& format, Args&& ... args );
+
+ template<typename... Args>
+ void debug( Args&& ... args );
+ template<typename... Args>
+ void debugf( const std::string& format, Args&& ... args );
+
+ template<typename... Args>
+ void note( Args&& ... args );
+ template<typename... Args>
+ void notef( const std::string& format, Args&& ... args );
+
+ template<typename... Args>
+ void warn( Args&& ... args );
+ template<typename... Args>
+ void warnf( const std::string& format, Args&& ... args );
+
+ template<typename... Args>
+ void error( Args&& ... args );
+ template<typename... Args>
+ void errorf( const std::string& format, Args&& ... args );
+
+ template<typename... Args>
+ void fatal( Args&& ... args );
+ template<typename... Args>
+ void fatalf( const std::string& format, Args&& ... args );
+
+ friend void impl::reassign_stack_pointer( logger_set*& ptr );
+ friend logger_set current_logger_extended( std::initializer_list<log_target> further_targets );
+ private:
+ void log_impl( level l, const std::string& msg );
+
+ std::vector<log_target> m_loggers;
+ logger_set** m_stackpointer = nullptr;
+ level m_min_level;
+ };
+
+ logger_set current_logger_extended( std::initializer_list<log_target> further_targets );
+
+ namespace impl {
+ std::string concat_msg( level l, const std::vector<std::string>& args );
+ std::string format_msg( level l, const std::string&, std::vector<std::string> args );
+ logger_set& active_logger();
+ }
+
+ template <typename... Args>
+ void logger_set::log( level l, Args&& ... data ) {
+ if( l < m_min_level ) {
+ return;
+ }
+
+ log_impl( l, impl::concat_msg( l, {conv::to_string( std::forward<Args>( data ) )...} ) );
+ }
+
+ template <typename... Args>
+ void logger_set::logf( level l, const std::string& format, Args&& ... data ) {
+ if( l < m_min_level ) {
+ return;
+ }
+
+ log_impl( l, impl::format_msg( l, format, {conv::to_string( std::forward<Args>( data ) )...} ) );
+ }
+
+ template <typename... Args>
+ void log( level l, Args&& ... args ) {
+ impl::active_logger().log( l, std::forward<Args>( args )... );
+ }
+ template <typename... Args>
+ void logf( level l, const std::string& format, Args&& ... args ) {
+ impl::active_logger().logf( l, format, std::forward<Args>( args )... );
+ }
+
+ // concat-based methods
+ template <typename... Args>
+ void logger_set::debug( Args&& ... args ) {
+ log( level::debug, std::forward<Args>( args )... );
+ }
+ template <typename... Args>
+ void logger_set::note( Args&& ... args ) {
+ log( level::note, std::forward<Args>( args )... );
+ }
+ template <typename... Args>
+ void logger_set::warn( Args&& ... args ) {
+ log( level::warn, std::forward<Args>( args )... );
+ }
+ template <typename... Args>
+ void logger_set::error( Args&& ... args ) {
+ log( level::error, std::forward<Args>( args )... );
+ }
+ template <typename... Args>
+ void logger_set::fatal( Args&& ... args ) {
+ log( level::fatal, std::forward<Args>( args )... );
+ }
+
+ // format-based methods
+ template <typename... Args>
+ void logger_set::debugf( const std::string& fmt, Args&& ... args ) {
+ logf( level::debug, fmt, std::forward<Args>( args )... );
+ }
+ template <typename... Args>
+ void logger_set::notef( const std::string& fmt, Args&& ... args ) {
+ logf( level::note, fmt, std::forward<Args>( args )... );
+ }
+ template <typename... Args>
+ void logger_set::warnf( const std::string& fmt, Args&& ... args ) {
+ logf( level::warn, fmt, std::forward<Args>( args )... );
+ }
+ template <typename... Args>
+ void logger_set::errorf( const std::string& fmt, Args&& ... args ) {
+ logf( level::error, fmt, std::forward<Args>( args )... );
+ }
+ template <typename... Args>
+ void logger_set::fatalf( const std::string& fmt, Args&& ... args ) {
+ logf( level::fatal, fmt, std::forward<Args>( args )... );
+ }
+
+ // global concat-based
+ template <typename... Args>
+ void debug( Args&& ... args ) {
+ log( level::debug, std::forward<Args>( args )... );
+ }
+ template <typename... Args>
+ void note( Args&& ... args ) {
+ log( level::note, std::forward<Args>( args )... );
+ }
+ template <typename... Args>
+ void warn( Args&& ... args ) {
+ log( level::warn, std::forward<Args>( args )... );
+ }
+ template <typename... Args>
+ void error( Args&& ... args ) {
+ log( level::error, std::forward<Args>( args )... );
+ }
+ template <typename... Args>
+ void fatal( Args&& ... args ) {
+ log( level::fatal, std::forward<Args>( args )... );
+ }
+
+ // global format-based
+ template <typename... Args>
+ void debugf( const std::string& fmt, Args&& ... args ) {
+ logf( level::debug, fmt, std::forward<Args>( args )... );
+ }
+ template <typename... Args>
+ void notef( const std::string& fmt, Args&& ... args ) {
+ logf( level::note, fmt, std::forward<Args>( args )... );
+ }
+ template <typename... Args>
+ void warnf( const std::string& fmt, Args&& ... args ) {
+ logf( level::warn, fmt, std::forward<Args>( args )... );
+ }
+ template <typename... Args>
+ void errorf( const std::string& fmt, Args&& ... args ) {
+ logf( level::error, fmt, std::forward<Args>( args )... );
+ }
+ template <typename... Args>
+ void fatalf( const std::string& fmt, Args&& ... args ) {
+ logf( level::fatal, fmt, std::forward<Args>( args )... );
+ }
+
+}
}
return std::pair<bool, time_t>( true, res );
-
}
std::pair<bool, time_t> parseMonthInterval( std::time_t t, const std::string& date ) {
return std::pair<bool, time_t>( false, 0 );
}
}
+
std::pair<bool, time_t> parseYearInterval( std::time_t t, const std::string& date ) {
if( date[date.size() - 1] != 'y' ) {
return std::pair<bool, time_t>( false, 0 );
}
}
-std::shared_ptr<std::ofstream> openLogfile( const std::string name) {
+std::shared_ptr<std::ofstream> openLogfile( const std::string name ) {
struct stat buffer;
std::string tname = name;
int ctr = 2;
- while(stat (tname.c_str(), &buffer) == 0) {
- tname = name + "_" + std::to_string(ctr++);
+
+ while( stat( tname.c_str(), &buffer ) == 0 ) {
+ tname = name + "_" + std::to_string( ctr++ );
}
- auto res = std::shared_ptr<std::ofstream>(new std::ofstream( tname ),
- [](std::ofstream *p){
+
+ auto res = std::shared_ptr<std::ofstream>( new std::ofstream( tname ),
+ []( std::ofstream * p ) {
p->close();
delete p;
- });
- if(! res->good() ){
- throw std::string("Failed to open file for logging: ") + name;
+ } );
+
+ if( ! res->good() ) {
+ throw std::string( "Failed to open file for logging: " ) + name;
}
+
return res;
}
std::pair<bool, std::time_t> parseMonthInterval( std::time_t t, const std::string& date );
std::pair<bool, std::time_t> parseYearInterval( std::time_t t, const std::string& date );
-
-std::shared_ptr<std::ofstream> openLogfile( const std::string name);
+std::shared_ptr<std::ofstream> openLogfile( const std::string name );
--- /dev/null
+#include <iostream>
+#include <sstream>
+#include <stdexcept>
+
+#include <boost/test/unit_test.hpp>
+
+#include "log/logger.hpp"
+#include "log/format.hpp"
+
+BOOST_AUTO_TEST_SUITE( TestLogger )
+
+static inline bool head_and_tail_equal( const std::string& str, const std::string& head, const std::string& tail ) {
+ return str.size() >= head.size() + tail.size()
+ and std::equal( head.begin(), head.end(), str.begin() )
+ and std::equal( tail.rbegin(), tail.rend(), str.rbegin() )
+ ;
+}
+
+BOOST_AUTO_TEST_CASE( basic_log ) {
+ std::ostringstream stream{};
+ auto logger = logger::logger_set{stream};
+
+ logger.log( logger::level::note, "foo", " bar ", 23, ' ', 42.0, " baz" );
+
+ BOOST_CHECK( head_and_tail_equal( stream.str(), "[note ][", "]: foo bar 23 42 baz\n" ) );
+}
+
+BOOST_AUTO_TEST_CASE( basic_logf ) {
+ std::ostringstream stream{};
+ auto logger = logger::logger_set{stream};
+
+ logger.logf( logger::level::note, "bla%sblub%s%%", "foo", 42 );
+
+ BOOST_CHECK( head_and_tail_equal( stream.str(), "[note ][", "]: blafooblub42%\n" ) );
+}
+
+BOOST_AUTO_TEST_CASE( log_hiding ) {
+ std::ostringstream stream1{};
+ auto logger1 = logger::logger_set{stream1};
+
+ std::ostringstream stream2{};
+ auto logger2 = logger::logger_set{stream2};
+
+ logger::note( "foobar" );
+
+ BOOST_CHECK( stream1.str().empty() );
+ BOOST_CHECK( head_and_tail_equal( stream2.str(), "[note ][", "]: foobar\n" ) );
+}
+
+BOOST_AUTO_TEST_CASE( log_restoration ) {
+ std::ostringstream stream1{};
+ auto logger1 = logger::logger_set{stream1};
+
+ {
+ std::ostringstream stream2{};
+ auto logger2 = logger::logger_set{stream2};
+ }
+
+ logger::note( "foobar" );
+
+ BOOST_CHECK( head_and_tail_equal( stream1.str(), "[note ][", "]: foobar\n" ) );
+}
+
+BOOST_AUTO_TEST_CASE( non_global_log ) {
+ std::ostringstream stream1{};
+ auto logger1 = logger::logger_set{stream1};
+
+ std::ostringstream stream2{};
+ auto logger2 = logger::logger_set{{stream2}, logger::auto_register::off};
+
+ logger::note( "foobar" );
+
+ BOOST_CHECK( head_and_tail_equal( stream1.str(), "[note ][", "]: foobar\n" ) );
+ BOOST_CHECK( stream2.str().empty() );
+}
+
+BOOST_AUTO_TEST_CASE( concat_alias_methods ) {
+ {
+ std::ostringstream stream{};
+ auto logger = logger::logger_set{{stream, logger::level::debug}};
+
+ logger.debug( "foo" );
+
+ BOOST_CHECK( head_and_tail_equal( stream.str(), "[debug][", "]: foo\n" ) );
+ }
+
+ {
+ std::ostringstream stream{};
+ auto logger = logger::logger_set{{stream, logger::level::note}};
+
+ logger.note( "foo" );
+
+ BOOST_CHECK( head_and_tail_equal( stream.str(), "[note ][", "]: foo\n" ) );
+ }
+
+ {
+ std::ostringstream stream{};
+ auto logger = logger::logger_set{{stream, logger::level::warn}};
+
+ logger.warn( "foo" );
+
+ BOOST_CHECK( head_and_tail_equal( stream.str(), "[warn ][", "]: foo\n" ) );
+ }
+
+ {
+ std::ostringstream stream{};
+ auto logger = logger::logger_set{{stream, logger::level::error}};
+
+ logger.error( "foo" );
+
+ BOOST_CHECK( head_and_tail_equal( stream.str(), "[error][", "]: foo\n" ) );
+ }
+
+ {
+ std::ostringstream stream{};
+ auto logger = logger::logger_set{{stream, logger::level::fatal}};
+
+ logger.fatal( "foo" );
+
+ BOOST_CHECK( head_and_tail_equal( stream.str(), "[fatal][", "]: foo\n" ) );
+ }
+}
+
+BOOST_AUTO_TEST_CASE( format_alias_methods ) {
+ {
+ std::ostringstream stream{};
+ auto logger = logger::logger_set{{stream, logger::level::debug}};
+
+ logger.debugf( "foo" );
+
+ BOOST_CHECK( head_and_tail_equal( stream.str(), "[debug][", "]: foo\n" ) );
+ }
+
+ {
+ std::ostringstream stream{};
+ auto logger = logger::logger_set{{stream, logger::level::note}};
+
+ logger.notef( "foo" );
+
+ BOOST_CHECK( head_and_tail_equal( stream.str(), "[note ][", "]: foo\n" ) );
+ }
+
+ {
+ std::ostringstream stream{};
+ auto logger = logger::logger_set{{stream, logger::level::warn}};
+
+ logger.warnf( "foo" );
+
+ BOOST_CHECK( head_and_tail_equal( stream.str(), "[warn ][", "]: foo\n" ) );
+ }
+
+ {
+ std::ostringstream stream{};
+ auto logger = logger::logger_set{{stream, logger::level::error}};
+
+ logger.errorf( "foo" );
+
+ BOOST_CHECK( head_and_tail_equal( stream.str(), "[error][", "]: foo\n" ) );
+ }
+
+ {
+ std::ostringstream stream{};
+ auto logger = logger::logger_set{{stream, logger::level::fatal}};
+
+ logger.fatalf( "foo" );
+
+ BOOST_CHECK( head_and_tail_equal( stream.str(), "[fatal][", "]: foo\n" ) );
+ }
+}
+
+BOOST_AUTO_TEST_CASE( concat_alias_functions ) {
+ {
+ std::ostringstream stream{};
+ auto logger = logger::logger_set{{stream, logger::level::debug}};
+
+ logger::debug( "foo" );
+
+ BOOST_CHECK( head_and_tail_equal( stream.str(), "[debug][", "]: foo\n" ) );
+ }
+
+ {
+ std::ostringstream stream{};
+ auto logger = logger::logger_set{{stream, logger::level::note}};
+
+ logger::note( "foo" );
+
+ BOOST_CHECK( head_and_tail_equal( stream.str(), "[note ][", "]: foo\n" ) );
+ }
+
+ {
+ std::ostringstream stream{};
+ auto logger = logger::logger_set{{stream, logger::level::warn}};
+
+ logger::warn( "foo" );
+
+ BOOST_CHECK( head_and_tail_equal( stream.str(), "[warn ][", "]: foo\n" ) );
+ }
+
+ {
+ std::ostringstream stream{};
+ auto logger = logger::logger_set{{stream, logger::level::error}};
+
+ logger::error( "foo" );
+
+ BOOST_CHECK( head_and_tail_equal( stream.str(), "[error][", "]: foo\n" ) );
+ }
+
+ {
+ std::ostringstream stream{};
+ auto logger = logger::logger_set{{stream, logger::level::fatal}};
+
+ logger::fatal( "foo" );
+
+ BOOST_CHECK( head_and_tail_equal( stream.str(), "[fatal][", "]: foo\n" ) );
+ }
+}
+
+BOOST_AUTO_TEST_CASE( format_alias_functions ) {
+ {
+ std::ostringstream stream{};
+ auto logger = logger::logger_set{{stream, logger::level::debug}};
+
+ logger::debugf( "foo" );
+
+ BOOST_CHECK( head_and_tail_equal( stream.str(), "[debug][", "]: foo\n" ) );
+ }
+
+ {
+ std::ostringstream stream{};
+ auto logger = logger::logger_set{{stream, logger::level::note}};
+
+ logger::notef( "foo" );
+
+ BOOST_CHECK( head_and_tail_equal( stream.str(), "[note ][", "]: foo\n" ) );
+ }
+
+ {
+ std::ostringstream stream{};
+ auto logger = logger::logger_set{{stream, logger::level::warn}};
+
+ logger::warnf( "foo" );
+
+ BOOST_CHECK( head_and_tail_equal( stream.str(), "[warn ][", "]: foo\n" ) );
+ }
+
+ {
+ std::ostringstream stream{};
+ auto logger = logger::logger_set{{stream, logger::level::error}};
+
+ logger::errorf( "foo" );
+
+ BOOST_CHECK( head_and_tail_equal( stream.str(), "[error][", "]: foo\n" ) );
+ }
+
+ {
+ std::ostringstream stream{};
+ auto logger = logger::logger_set{{stream, logger::level::fatal}};
+
+ logger::fatalf( "foo" );
+
+ BOOST_CHECK( head_and_tail_equal( stream.str(), "[fatal][", "]: foo\n" ) );
+ }
+}
+
+BOOST_AUTO_TEST_CASE( formatting_exceptions ) {
+ std::ostringstream stream{};
+ auto logger = logger::logger_set{stream};
+
+ BOOST_CHECK_THROW( logger.notef( "%" ), std::invalid_argument );
+ BOOST_CHECK_THROW( logger.notef( "%s" ), std::invalid_argument );
+ BOOST_CHECK_THROW( logger.notef( "%e" ), std::invalid_argument );
+}
+
+BOOST_AUTO_TEST_CASE( multiple_calls ) {
+ std::ostringstream stream{};
+ auto logger = logger::logger_set{stream};
+
+ logger::note( "foo1" );
+ logger::debug( "foo2" );
+ logger::warn( "foo3" );
+ logger::note( "foo4" );
+
+ const auto result = stream.str();
+
+ const auto foo1 = result.find( "foo1" );
+ const auto foo2 = result.find( "foo2" );
+ const auto foo3 = result.find( "foo3" );
+ const auto foo4 = result.find( "foo4" );
+
+ BOOST_CHECK_LT( foo1, foo3 );
+ BOOST_CHECK_LT( foo3, foo4 );
+ BOOST_CHECK_NE( foo4, std::string::npos );
+ BOOST_CHECK_EQUAL( foo2, std::string::npos );
+}
+
+BOOST_AUTO_TEST_CASE( multiple_calls_nested ) {
+ std::ostringstream stream{};
+ auto logger = logger::logger_set{stream};
+
+ logger::note( "foo1" );
+
+ {
+ std::ostringstream stream{};
+ auto logger = logger::logger_set{stream};
+
+ logger::note( "foo2" );
+ }
+
+ logger::note( "foo3" );
+
+ const auto result = stream.str();
+ const auto foo1 = result.find( "foo1" );
+ const auto foo2 = result.find( "foo2" );
+ const auto foo3 = result.find( "foo3" );
+
+ BOOST_CHECK_LT( foo1, foo3 );
+ BOOST_CHECK_NE( foo3, std::string::npos );
+ BOOST_CHECK_EQUAL( foo2, std::string::npos );
+}
+
+BOOST_AUTO_TEST_CASE( extending_current_logger ) {
+ std::ostringstream stream1{};
+ auto logger1 = logger::logger_set{stream1};
+
+ std::ostringstream stream2{};
+ {
+ auto logger2 = logger::current_logger_extended( {stream2} );
+ logger::note( "foo1" );
+ }
+
+ BOOST_CHECK( head_and_tail_equal( stream1.str(), "[note ][", "]: foo1\n" ) );
+ BOOST_CHECK( head_and_tail_equal( stream2.str(), "[note ][", "]: foo1\n" ) );
+
+ stream1.str( "" );
+ stream2.str( "" );
+
+ logger::note( "foo2" );
+
+ BOOST_CHECK( head_and_tail_equal( stream1.str(), "[note ][", "]: foo2\n" ) );
+ BOOST_CHECK( stream2.str().empty() );
+}
+
+BOOST_AUTO_TEST_CASE( closed_filestream_exception ) {
+ std::ofstream stream;
+
+ BOOST_CHECK_THROW( logger::logger_set {stream}, std::runtime_error );
+}
+
+BOOST_AUTO_TEST_CASE( formated_strings ) {
+ using namespace logger::format::literals;
+ using logger::conv::to_string;
+
+ BOOST_CHECK_EQUAL( to_string( ""_fmt( "foo" ) ), "foo" );
+ BOOST_CHECK_EQUAL( to_string( "_3"_fmt( "foo" ) ), "foo" );
+ BOOST_CHECK_EQUAL( to_string( "_6"_fmt( "foo" ) ), "foo___" );
+ BOOST_CHECK_EQUAL( to_string( "_10l"_fmt( "foo" ) ), "foo_______" );
+ BOOST_CHECK_EQUAL( to_string( "_10r"_fmt( "foo" ) ), "_______foo" );
+}
+
+BOOST_AUTO_TEST_CASE( formated_ints ) {
+ using namespace logger::format::literals;
+ using logger::conv::to_string;
+
+ BOOST_CHECK_EQUAL( to_string( ""_fmt( 3 ) ), "3" );
+ BOOST_CHECK_EQUAL( to_string( "03"_fmt( 3 ) ), "003" );
+ BOOST_CHECK_EQUAL( to_string( "03"_fmt( 13 ) ), "013" );
+ BOOST_CHECK_EQUAL( to_string( "03x"_fmt( 13 ) ), "00d" );
+ BOOST_CHECK_EQUAL( to_string( "03o"_fmt( 13 ) ), "015" );
+ BOOST_CHECK_EQUAL( to_string( "03d"_fmt( 13 ) ), "013" );
+ BOOST_CHECK_EQUAL( to_string( "03s"_fmt( 13 ) ), "013" );
+}
+
+BOOST_AUTO_TEST_CASE( formated_ints_variadic_api ) {
+ using logger::conv::to_string;
+ using logger::format::fmt;
+
+ BOOST_CHECK_EQUAL( to_string( fmt( 3 ) ), "3" );
+ BOOST_CHECK_EQUAL( to_string( fmt( 3, logger::format::width_t {3} ) ), " 3" );
+}
+
+BOOST_AUTO_TEST_CASE( formated_ints_variadic_api_literals ) {
+ using logger::conv::to_string;
+ using logger::format::fmt;
+
+ using namespace logger::format::literals;
+
+ BOOST_CHECK_EQUAL( to_string( fmt( 3 ) ), "3" );
+ BOOST_CHECK_EQUAL( to_string( fmt( 3, 3_w ) ), " 3" );
+ BOOST_CHECK_EQUAL( to_string( fmt( 10, 3_w, 8_b, 'x'_f ) ), "x12" );
+}
+
+BOOST_AUTO_TEST_SUITE_END()