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
RevLine 
[319]1// Copyright (C) 2011-2014 ChaosForge Ltd
[4]2// This file is part of NV Libraries.
3// For conditions of distribution and use, see copyright notice in nv.hh
4
[319]5#include "nv/core/logger.hh"
[4]6
[319]7#include "nv/core/common.hh"
[365]8#include "nv/core/time.hh"
[48]9#include <cstdio>
10#if NV_PLATFORM == NV_WINDOWS
11#define WIN32_LEAN_AND_MEAN
12#include <Windows.h>
13#endif
[4]14
15using namespace nv;
16
17// log level names
[121]18static const char *log_level_names[] =
[4]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
[48]33// log level names
[121]34static const char *log_level_names_pad[] =
[48]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
[4]49// helper macro to access log_level_names
50#define NV_LOG_LEVEL_NAME(level) (log_level_names[ (level) / 10 ])
[48]51#define NV_LOG_LEVEL_NAME_PAD(level) (log_level_names_pad[ (level) / 10 ])
[4]52
[48]53#if NV_PLATFORM == NV_WINDOWS
[121]54static unsigned short log_color[] =
[48]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
[121]69static const char *log_color[] =
[48]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
[4]85// log function
[365]86void logger::log( log_level level, const string_ref& message )
[4]87{
[365]88        for ( auto& sink_info : m_log_sinks )
[4]89        {
[365]90                if ( sink_info.sink && (sink_info.level >= (uint32)level) )
[4]91                {
92                        // log and iterate
[365]93                        sink_info.sink->log( level, message );
[4]94                }
95        }
96}
97
98// add a new sink
99void logger::add_sink( log_sink* sink, int level )
100{
101        // add a sink
[365]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!" );
[4]112}
113
114// remove existing sink
115bool logger::remove_sink( log_sink* sink )
116{
[365]117        for ( auto& sink_info : m_log_sinks )
[4]118        {
[365]119                if ( sink_info.sink == sink )
[4]120                {
[365]121                        delete sink_info.sink;
122                        sink_info.sink = nullptr;
[4]123                        return true;
124                }
125        }
126        // not found, return false
127        return false;
128}
129
130// destructor
131logger::~logger()
132{
[365]133        // delete all sinks
134        for ( auto& sink_info : m_log_sinks )
[4]135        {
[365]136                delete sink_info.sink;
[4]137        }
138}
139
140
141// console logging
[365]142void log_console_sink::log( log_level level, const string_ref& message )
[4]143{
[48]144#if NV_PLATFORM == NV_WINDOWS
[365]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 );
[48]156#else
[365]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 );
[48]164        }
[365]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 );
[48]168        else
[365]169                fwrite( "] ", 2, 1, stdout );
170        fwrite( stamp.data(), stamp.size(), 1, stdout );
171        fwrite( "\n", 1, 1, stdout );
172#endif
[4]173}
174
[365]175// handle logging
176void log_handle_sink::log( log_level level, const string_ref& message )
[4]177{
[365]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
[4]200}
201
[365]202nv::log_file_sink::log_file_sink( const string_ref& file_name, bool flush_always /*= true */ )
203        : log_handle_sink( nullptr, flush_always )
[4]204{
[365]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 )
[4]209        {
[365]210                NV_ASSERT( false, "invalid log handle" );
[4]211        }
[365]212        m_handle = handle;
213#else
214        m_handle = fopen( file_name.data(), "w" );
215#endif
[4]216}
217
[365]218nv::log_file_sink::~log_file_sink()
[4]219{
[365]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
[4]226}
[48]227
[365]228
[48]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 );
[204]234#else
235  NV_UNUSED( m_handle );
[48]236#endif
237}
238
[365]239string_ref nv::log_sink::timestamp() const
[48]240{
[365]241        uint32 ms = get_system_ms();
242        unsigned int secs = (unsigned int)(ms / 1000);
243        unsigned int mm   = (unsigned int)( ms * 100 / 1000 ) % 100;
[48]244        unsigned int h    = (unsigned int)(secs / (60*60));
245        unsigned int m    = (unsigned int)(secs / 60) % 60;
246        unsigned int s    = secs % 60;
[365]247        static char buffer[16];
[48]248#if NV_PLATFORM == NV_WINDOWS
[365]249        sprintf_s( buffer, 16, "%02d:%02d:%02d.%02d", h, m, s, mm );
[48]250#else
[365]251        snprintf( buffer, 16, "%02d:%02d:%02d.%02d", h, m, s, mm );
[48]252#endif
253        buffer[11] = '\0';
[365]254        return string_ref( buffer, 10 );
[48]255}
[295]256
[365]257string_ref nv::log_sink::level_name( log_level level ) const
[295]258{
259        return NV_LOG_LEVEL_NAME( level );
260}
261
[365]262string_ref nv::log_sink::padded_level_name( log_level level ) const
[295]263{
[365]264        return string_ref( NV_LOG_LEVEL_NAME_PAD( level ), 8 );
[295]265}
[365]266
Note: See TracBrowser for help on using the repository browser.