]> WPIA git - cassiopeia.git/blob - src/log/logger.cpp
add: Import Logging functionality by Florian Weber
[cassiopeia.git] / src / log / logger.cpp
1 #include "log/logger.hpp"
2
3 #include <algorithm>
4 #include <chrono>
5 #include <cstring>
6 #include <iostream>
7 #include <iterator>
8
9 namespace logger {
10
11     namespace {
12
13         std::ostream*& ostream_pointer() {
14             static std::ostream* stream = &std::cout;
15             return stream;
16         }
17
18         std::ostream& get_stream() {
19             return *ostream_pointer();
20         }
21
22         std::string make_prefix( level l ) {
23             auto prefix = std::string {};
24
25             switch( l ) {
26             case level::debug:
27                 prefix = "[debug][";
28                 break;
29
30             case level::note:
31                 prefix = "[note ][";
32                 break;
33
34             case level::warn:
35                 prefix = "[warn ][";
36                 break;
37
38             case level::error:
39                 prefix = "[error][";
40                 break;
41
42             case level::fatal:
43                 prefix = "[fatal][";
44                 break;
45             }
46
47             using clock = std::chrono::system_clock;
48             const auto now = clock::to_time_t( clock::now() );
49             // ctime appends a newline, we don't want that here:
50             auto time_str = std::ctime( &now );
51             prefix.append( time_str, time_str + std::strlen( time_str ) - 1 );
52             prefix += "]: ";
53             return prefix;
54         }
55
56     } // anonymous namespace
57
58     namespace impl {
59
60         std::string replace_newlines( const std::string& str, std::size_t length ) {
61             auto returnstring = std::string {};
62             auto it = str.begin();
63             const auto end = str.end();
64             auto nl_it = it;
65
66             while( ( nl_it = std::find( it, end, '\n' ) ) != end ) {
67                 ++nl_it;
68                 returnstring.append( it, nl_it );
69                 returnstring.append( length, ' ' );
70                 it = nl_it;
71             }
72
73             returnstring.append( it, end );
74             return returnstring;
75         }
76
77         void log( level l, const std::vector<std::string>& args ) {
78             const auto prefix = make_prefix( l );
79             const auto length = prefix.length();
80             get_stream() << prefix;
81             std::transform( args.begin(), args.end(), std::ostream_iterator<std::string> {get_stream()},
82                 [length]( const std::string & str ) {
83                     return replace_newlines( str, length );
84                 } );
85             get_stream() << '\n' << std::flush;
86         }
87
88         void logf( level l, const std::string& format, std::vector<std::string> args ) {
89             const auto prefix = make_prefix( l );
90             const auto length = prefix.length();
91             const auto fmt = replace_newlines( format, length );
92             std::transform( args.begin(), args.end(), args.begin(),
93                 [length]( const std::string & str ) {
94                     return replace_newlines( str, length );
95                 } );
96
97             auto mesg = prefix;
98             auto arg_index = std::size_t {0};
99             auto it = fmt.begin();
100             const auto end = fmt.end();
101
102             while( it != end ) {
103                 auto pos = std::find( it, end, '%' );
104                 mesg.append( it, pos );
105
106                 if( pos == end ) {
107                     break;
108                 }
109
110                 ++pos;
111
112                 if( pos == end ) {
113                     throw std::invalid_argument {"Invalid formatstring (ends on single '%')"};
114                 }
115
116                 switch( *pos ) {
117                 case '%':
118                     mesg.push_back( '%' );
119                     break;
120
121                 case 's':
122                     if( arg_index >= args.size() ) {
123                         throw std::invalid_argument {"Invalid formatstring (not enough arguments)"};
124                     }
125
126                     mesg.append( args[arg_index++] );
127                     break;
128
129                 default:
130                     throw std::invalid_argument {"Invalid formatstring (unknown format-character)"};
131                 }
132
133                 it = std::next( pos );
134             }
135
136             mesg.push_back( '\n' );
137             get_stream() << mesg << std::flush;
138         }
139
140     } //  namespace impl
141
142     void set_stream( std::ostream& stream ) {
143         ostream_pointer() = &stream;
144     }
145
146 } // namespace logger