source: trunk/src/core/logger.cc @ 365

Last change on this file since 365 was 365, checked in by epyon, 10 years ago
  • overhaul of logging: no longer stream operated no longer using STL no memory allocation shorthand macros fast file and console I/O
File size: 7.0 KB
Line 
1// Copyright (C) 2011-2014 ChaosForge Ltd
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/core/logger.hh"
6
7#include "nv/core/common.hh"
8#include "nv/core/time.hh"
9#include <cstdio>
10#if NV_PLATFORM == NV_WINDOWS
11#define WIN32_LEAN_AND_MEAN
12#include <Windows.h>
13#endif
14
15using namespace nv;
16
17// log level names
18static const char *log_level_names[] =
19{
20        "NONE",
21        "FATAL",
22        "CRITICAL",
23        "ERROR",
24        "WARNING",
25        "NOTICE",
26        "INFO",
27        "INFO",
28        "DEBUG",
29        "DEBUG2",
30        "TRACE"
31};
32
33// log level names
34static const char *log_level_names_pad[] =
35{
36        "NONE    ",
37        "FATAL   ",
38        "CRITICAL",
39        "ERROR   ",
40        "WARNING ",
41        "NOTICE  ",
42        "INFO    ",
43        "INFO    ",
44        "DEBUG   ",
45        "DEBUG2  ",
46        "TRACE   "
47};
48
49// helper macro to access log_level_names
50#define NV_LOG_LEVEL_NAME(level) (log_level_names[ (level) / 10 ])
51#define NV_LOG_LEVEL_NAME_PAD(level) (log_level_names_pad[ (level) / 10 ])
52
53#if NV_PLATFORM == NV_WINDOWS
54static unsigned short log_color[] =
55{
56        FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY,
57        FOREGROUND_RED | FOREGROUND_INTENSITY,
58        FOREGROUND_RED | FOREGROUND_INTENSITY,
59        FOREGROUND_RED,
60        FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY,
61        FOREGROUND_GREEN | FOREGROUND_INTENSITY,
62        FOREGROUND_GREEN,
63        FOREGROUND_GREEN,
64        FOREGROUND_INTENSITY,
65        FOREGROUND_INTENSITY,
66        FOREGROUND_INTENSITY
67};
68#else
69static const char *log_color[] =
70{
71        "\33[37;1m",
72        "\33[31;1m",
73        "\33[31;1m",
74        "\33[31m",
75        "\33[33;1m",
76        "\33[32;1m",
77        "\33[32m",
78        "\33[32m",
79        "\33[30;1m",
80        "\33[30;1m",
81        "\33[30;1m"
82};
83#endif
84
85// log function
86void logger::log( log_level level, const string_ref& message )
87{
88        for ( auto& sink_info : m_log_sinks )
89        {
90                if ( sink_info.sink && (sink_info.level >= (uint32)level) )
91                {
92                        // log and iterate
93                        sink_info.sink->log( level, message );
94                }
95        }
96}
97
98// add a new sink
99void logger::add_sink( log_sink* sink, int level )
100{
101        // add a sink
102        for ( auto& sink_info : m_log_sinks )
103        {
104                if ( sink_info.sink == nullptr )
105                {
106                        sink_info.sink  = sink;
107                        sink_info.level = (uint32)level;
108                        return;
109                }
110        }
111        NV_ASSERT( false, "ran out of log sink space!" );
112}
113
114// remove existing sink
115bool logger::remove_sink( log_sink* sink )
116{
117        for ( auto& sink_info : m_log_sinks )
118        {
119                if ( sink_info.sink == sink )
120                {
121                        delete sink_info.sink;
122                        sink_info.sink = nullptr;
123                        return true;
124                }
125        }
126        // not found, return false
127        return false;
128}
129
130// destructor
131logger::~logger()
132{
133        // delete all sinks
134        for ( auto& sink_info : m_log_sinks )
135        {
136                delete sink_info.sink;
137        }
138}
139
140
141// console logging
142void log_console_sink::log( log_level level, const string_ref& message )
143{
144#if NV_PLATFORM == NV_WINDOWS
145        if ( m_color ) SetConsoleTextAttribute( m_handle, FOREGROUND_INTENSITY );
146        string_ref stamp( timestamp() );
147        WriteConsole( m_handle, stamp.data(), stamp.size(), nullptr, nullptr );
148        WriteConsole( m_handle, " [", 2, nullptr, nullptr );
149        if (m_color) SetConsoleTextAttribute( m_handle, log_color[( level ) / 10] );
150        WriteConsole( m_handle, NV_LOG_LEVEL_NAME_PAD( level ), 8, nullptr, nullptr );
151        if ( m_color ) SetConsoleTextAttribute( m_handle, FOREGROUND_INTENSITY );
152        WriteConsole( m_handle, "] ", 2, nullptr, nullptr );
153        if ( m_color ) SetConsoleTextAttribute( m_handle, FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE );
154        WriteConsole( m_handle, message.data(), message.size(), nullptr, nullptr );
155        WriteConsole( m_handle, "\n", 1, nullptr, nullptr );
156#else
157        if ( m_color ) fwrite( "\33[30;1m", 7, 1, stdout );
158        fwrite( stamp.data(), stamp.size(), 1, stdout );
159        fwrite( " [", 2, 1, stdout );
160        if ( m_color )
161        {
162                const char* lcolor = log_color[( level ) / 10];
163                fwrite( lcolor, strlen(lcolor), 1, stdout );
164        }
165        fwrite( NV_LOG_LEVEL_NAME_PAD( level ), 8, 1, stdout );
166        if ( m_color )
167                fwrite( "\33[30;1m] \33[37;1m", 16, 1, stdout );
168        else
169                fwrite( "] ", 2, 1, stdout );
170        fwrite( stamp.data(), stamp.size(), 1, stdout );
171        fwrite( "\n", 1, 1, stdout );
172#endif
173}
174
175// handle logging
176void log_handle_sink::log( log_level level, const string_ref& message )
177{
178        string_ref stamp( timestamp() );
179#if 0 // NV_PLATFORM == NV_WINDOWS
180        // Turns out WriteFile on Windows is unbuffered and quite slower than fwrite
181        // due to this fact -- especially UNUSABLE with manual FlushFileBuffers
182        // If we want to get rid of C runtime, this would need a buffered I/O layer.
183        DWORD unused = 0;
184        WriteFile( m_handle, stamp.data(), stamp.size(), &unused, nullptr );
185        WriteFile( m_handle, " [", 2, &unused, nullptr );
186        WriteFile( m_handle, NV_LOG_LEVEL_NAME_PAD( level ), 8, &unused, nullptr );
187        WriteFile( m_handle, "] ", 2, &unused, nullptr );
188        WriteFile( m_handle, message.data(), message.size(), &unused, nullptr );
189        WriteFile( m_handle, "\n", 1, &unused, nullptr );
190        //if ( m_flush ) FlushFileBuffers( m_handle );
191#else
192        fwrite( stamp.data(), stamp.size(), 1, (FILE*)m_handle );
193        fwrite( " [", 2, 1, (FILE*)m_handle );
194        fwrite( NV_LOG_LEVEL_NAME_PAD( level ), 8, 1, (FILE*)m_handle );
195        fwrite( "] ", 2, 1, (FILE*)m_handle );
196        fwrite( message.data(), message.size(), 1, (FILE*)m_handle );
197        fwrite( "\n", 1, 1, (FILE*)m_handle );
198        if ( m_flush ) fflush( (FILE*)m_handle );
199#endif
200}
201
202nv::log_file_sink::log_file_sink( const string_ref& file_name, bool flush_always /*= true */ )
203        : log_handle_sink( nullptr, flush_always )
204{
205#if 0 // NV_PLATFORM == NV_WINDOWS
206        // See comments in log_handle_sink
207        HANDLE handle = CreateFile( file_name.data(), GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr );
208        if ( INVALID_HANDLE_VALUE == handle )
209        {
210                NV_ASSERT( false, "invalid log handle" );
211        }
212        m_handle = handle;
213#else
214        m_handle = fopen( file_name.data(), "w" );
215#endif
216}
217
218nv::log_file_sink::~log_file_sink()
219{
220#if 0 // NV_PLATFORM == NV_WINDOWS
221        // See comments in log_handle_sink
222        CloseHandle( m_handle );
223#else
224        fclose( (FILE*) m_handle );
225#endif
226}
227
228
229nv::log_console_sink::log_console_sink( bool coloring )
230        : m_color( coloring )
231{
232#if NV_PLATFORM == NV_WINDOWS
233        m_handle = GetStdHandle( STD_OUTPUT_HANDLE );
234#else
235  NV_UNUSED( m_handle );
236#endif
237}
238
239string_ref nv::log_sink::timestamp() const
240{
241        uint32 ms = get_system_ms();
242        unsigned int secs = (unsigned int)(ms / 1000);
243        unsigned int mm   = (unsigned int)( ms * 100 / 1000 ) % 100;
244        unsigned int h    = (unsigned int)(secs / (60*60));
245        unsigned int m    = (unsigned int)(secs / 60) % 60;
246        unsigned int s    = secs % 60;
247        static char buffer[16];
248#if NV_PLATFORM == NV_WINDOWS
249        sprintf_s( buffer, 16, "%02d:%02d:%02d.%02d", h, m, s, mm );
250#else
251        snprintf( buffer, 16, "%02d:%02d:%02d.%02d", h, m, s, mm );
252#endif
253        buffer[11] = '\0';
254        return string_ref( buffer, 10 );
255}
256
257string_ref nv::log_sink::level_name( log_level level ) const
258{
259        return NV_LOG_LEVEL_NAME( level );
260}
261
262string_ref nv::log_sink::padded_level_name( log_level level ) const
263{
264        return string_ref( NV_LOG_LEVEL_NAME_PAD( level ), 8 );
265}
266
Note: See TracBrowser for help on using the repository browser.