source: trunk/src/logger.cc @ 61

Last change on this file since 61 was 48, checked in by epyon, 12 years ago
  • coloring for logger
File size: 5.8 KB
Line 
1// Copyright (C) 2011 Kornel Kisielewicz
2// This file is part of NV Libraries.
3// For conditions of distribution and use, see copyright notice in nv.hh
4
5#include "nv/logger.hh"
6
7#include <iostream>
8#include <utility>
9#include <algorithm>
10#include <fstream>
11#include <ctime>
12#include <cstdio>
13#include <nv/exception.hh>
14#if NV_PLATFORM == NV_WINDOWS
15#define WIN32_LEAN_AND_MEAN
16#include <Windows.h>
17#endif
18
19using namespace nv;
20
21// log level names
22const char *log_level_names[] =
23{
24        "NONE",
25        "FATAL",
26        "CRITICAL",
27        "ERROR",
28        "WARNING",
29        "NOTICE",
30        "INFO",
31        "INFO",
32        "DEBUG",
33        "DEBUG2",
34        "TRACE"
35};
36
37// log level names
38const char *log_level_names_pad[] =
39{
40        "NONE    ",
41        "FATAL   ",
42        "CRITICAL",
43        "ERROR   ",
44        "WARNING ",
45        "NOTICE  ",
46        "INFO    ",
47        "INFO    ",
48        "DEBUG   ",
49        "DEBUG2  ",
50        "TRACE   "
51};
52
53// helper macro to access log_level_names
54#define NV_LOG_LEVEL_NAME(level) (log_level_names[ (level) / 10 ])
55#define NV_LOG_LEVEL_NAME_PAD(level) (log_level_names_pad[ (level) / 10 ])
56
57#if NV_PLATFORM == NV_WINDOWS
58unsigned short log_color[] =
59{
60        FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY,
61        FOREGROUND_RED | FOREGROUND_INTENSITY,
62        FOREGROUND_RED | FOREGROUND_INTENSITY,
63        FOREGROUND_RED,
64        FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY,
65        FOREGROUND_GREEN | FOREGROUND_INTENSITY,
66        FOREGROUND_GREEN,
67        FOREGROUND_GREEN,
68        FOREGROUND_INTENSITY,
69        FOREGROUND_INTENSITY,
70        FOREGROUND_INTENSITY
71};
72#else
73const char *log_color[] =
74{
75        "\33[37;1m",
76        "\33[31;1m",
77        "\33[31;1m",
78        "\33[31m",
79        "\33[33;1m",
80        "\33[32;1m",
81        "\33[32m",
82        "\33[32m",
83        "\33[30;1m",
84        "\33[30;1m",
85        "\33[30;1m"
86};
87#endif
88
89// log function
90void logger::log( log_level level, const std::string& message )
91{
92        // get the iterator to the beginning of the log_sink list
93        log_sink_list::reverse_iterator it = m_log_sinks.rbegin();
94
95        // iterate
96        while ( it != m_log_sinks.rend() )
97        {
98                // if we have a log sink with high enough level...
99                if ( it->first >= level )
100                {
101                        // log and iterate
102                        it->second->log( level, message );
103                }
104                else
105                {
106                        // otherwise return, the list is sorted by log level
107                        return;
108                }
109                ++it;
110        }
111}
112
113// add a new sink
114void logger::add_sink( log_sink* sink, int level )
115{
116        // add a sink
117        m_log_sinks.push_back( std::make_pair( log_level(level), sink ) );
118        // and sort the list (default sort of pairs is by first element)
119        m_log_sinks.sort();
120}
121
122// remove existing sink
123bool logger::remove_sink( log_sink* sink )
124{
125        // get the iterator to the beginning of the log_sink list
126        log_sink_list::iterator it = m_log_sinks.begin();
127
128        // iterate
129        while ( it != m_log_sinks.end() )
130        {
131                // found?
132                if ( it->second == sink )
133                {
134                        // erase and return true to report success
135                        m_log_sinks.erase(it);
136                        return true;
137                }
138                ++it;
139        }
140
141        // not found, return false
142        return false;
143}
144
145// destructor
146logger::~logger()
147{
148        // while we have sinks
149        while ( !m_log_sinks.empty() )
150        {
151                // delete the last one
152                delete m_log_sinks.back().second;
153                // and pop it
154                m_log_sinks.pop_back();
155        }
156}
157
158
159// console logging
160void log_console_sink::log( log_level level, const std::string& message )
161{
162        if (m_color)
163        {
164#if NV_PLATFORM == NV_WINDOWS
165                SetConsoleTextAttribute( m_handle, FOREGROUND_INTENSITY );
166                std::cout << timestamp() << " [";
167                SetConsoleTextAttribute( m_handle, log_color[ (level) / 10 ] );
168                std::cout << NV_LOG_LEVEL_NAME_PAD(level);
169                SetConsoleTextAttribute( m_handle, FOREGROUND_INTENSITY );
170                std::cout << "] ";
171                SetConsoleTextAttribute( m_handle, FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE );
172                std::cout << message << std::endl;
173#else
174                std::cout << "\33[30;1m" << timestamp() << " [" << log_color[ (level) / 10 ] << NV_LOG_LEVEL_NAME_PAD(level) << "\33[30;1m] \33[37;1m" << message << std::endl;
175#endif
176        }
177        else
178        {
179        std::cout << timestamp() << " [" << NV_LOG_LEVEL_NAME_PAD(level) << "] " << message << std::endl;
180        }
181}
182
183// stream logging
184void log_stream_sink::log( log_level level, const std::string& message )
185{
186        // if flushing is enabled
187        if ( m_flush )
188        {
189                // write and flush using std::endl
190                *m_stream << timestamp() << " [" << NV_LOG_LEVEL_NAME(level) << "] " << message << std::endl;
191        }
192        else
193        {
194                // write and end with "\n" (no flush)
195                *m_stream << timestamp() << " [" << NV_LOG_LEVEL_NAME(level) << "] " << message << "\n";
196        }
197}
198
199// file logging
200log_file_sink::log_file_sink( const std::string file_name, bool flush_always /*= true */ )
201        : log_stream_sink( nullptr, flush_always )
202{
203        // create the stream manually
204        std::ofstream* fstream = new std::ofstream( file_name );
205
206        // check if it's open
207        if ( !fstream->is_open() )
208        {
209                // throw if not open
210                throw runtime_error( "Could not open file \""+file_name+"\" for logging!" );
211        }
212
213        m_stream = fstream;
214}
215
216// file logger destructor
217log_file_sink::~log_file_sink()
218{
219        // close the file
220        dynamic_cast< std::ofstream* >(m_stream)->close();
221        // dispose of the stream
222        delete m_stream;
223}
224
225nv::log_console_sink::log_console_sink( bool coloring )
226        : m_color( coloring )
227{
228#if NV_PLATFORM == NV_WINDOWS
229        m_handle = GetStdHandle( STD_OUTPUT_HANDLE );
230#endif
231}
232
233const char* nv::log_sink::timestamp() const
234{
235        std::clock_t time = std::clock();
236        unsigned int secs = (unsigned int)(time / CLOCKS_PER_SEC);
237        unsigned int mm   = (unsigned int)(time*100 / CLOCKS_PER_SEC) % 100;
238        unsigned int h    = (unsigned int)(secs / (60*60));
239        unsigned int m    = (unsigned int)(secs / 60) % 60;
240        unsigned int s    = secs % 60;
241        static char buffer[128];
242#if NV_PLATFORM == NV_WINDOWS
243        sprintf_s( buffer, 128, "%02d:%02d:%02d.%02d", h, m, s, mm );
244#else
245        sprintf( buffer, "%02d:%02d:%02d.%02d", h, m, s, mm );
246#endif
247        buffer[11] = '\0';
248        return buffer;
249}
Note: See TracBrowser for help on using the repository browser.