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

Last change on this file since 368 was 368, checked in by epyon, 10 years ago
  • massive restructuring
  • detail::data_base class for container/reference class base
File size: 6.9 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{
[368]144        char stamp[16];
145        size_t ssize = timestamp( stamp );
146
[48]147#if NV_PLATFORM == NV_WINDOWS
[365]148        if ( m_color ) SetConsoleTextAttribute( m_handle, FOREGROUND_INTENSITY );
[368]149        WriteConsole( m_handle, stamp, ssize, nullptr, nullptr );
[365]150        WriteConsole( m_handle, " [", 2, nullptr, nullptr );
151        if (m_color) SetConsoleTextAttribute( m_handle, log_color[( level ) / 10] );
152        WriteConsole( m_handle, NV_LOG_LEVEL_NAME_PAD( level ), 8, nullptr, nullptr );
153        if ( m_color ) SetConsoleTextAttribute( m_handle, FOREGROUND_INTENSITY );
154        WriteConsole( m_handle, "] ", 2, nullptr, nullptr );
155        if ( m_color ) SetConsoleTextAttribute( m_handle, FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE );
156        WriteConsole( m_handle, message.data(), message.size(), nullptr, nullptr );
157        WriteConsole( m_handle, "\n", 1, nullptr, nullptr );
[48]158#else
[365]159        if ( m_color ) fwrite( "\33[30;1m", 7, 1, stdout );
[368]160        fwrite( stamp, ssize, 1, stdout );
[365]161        fwrite( " [", 2, 1, stdout );
162        if ( m_color )
163        {
164                const char* lcolor = log_color[( level ) / 10];
165                fwrite( lcolor, strlen(lcolor), 1, stdout );
[48]166        }
[365]167        fwrite( NV_LOG_LEVEL_NAME_PAD( level ), 8, 1, stdout );
168        if ( m_color )
169                fwrite( "\33[30;1m] \33[37;1m", 16, 1, stdout );
[48]170        else
[365]171                fwrite( "] ", 2, 1, stdout );
172        fwrite( stamp.data(), stamp.size(), 1, stdout );
173        fwrite( "\n", 1, 1, stdout );
174#endif
[4]175}
176
[365]177// handle logging
178void log_handle_sink::log( log_level level, const string_ref& message )
[4]179{
[368]180        char stamp[16];
181        size_t ssize = timestamp( stamp );
[365]182#if 0 // NV_PLATFORM == NV_WINDOWS
183        // Turns out WriteFile on Windows is unbuffered and quite slower than fwrite
184        // due to this fact -- especially UNUSABLE with manual FlushFileBuffers
185        // If we want to get rid of C runtime, this would need a buffered I/O layer.
186        DWORD unused = 0;
[368]187        WriteFile( m_handle, stamp, ssize, &unused, nullptr );
[365]188        WriteFile( m_handle, " [", 2, &unused, nullptr );
189        WriteFile( m_handle, NV_LOG_LEVEL_NAME_PAD( level ), 8, &unused, nullptr );
190        WriteFile( m_handle, "] ", 2, &unused, nullptr );
191        WriteFile( m_handle, message.data(), message.size(), &unused, nullptr );
192        WriteFile( m_handle, "\n", 1, &unused, nullptr );
193        //if ( m_flush ) FlushFileBuffers( m_handle );
194#else
[368]195        fwrite( stamp, ssize, 1, (FILE*)m_handle );
[365]196        fwrite( " [", 2, 1, (FILE*)m_handle );
197        fwrite( NV_LOG_LEVEL_NAME_PAD( level ), 8, 1, (FILE*)m_handle );
198        fwrite( "] ", 2, 1, (FILE*)m_handle );
199        fwrite( message.data(), message.size(), 1, (FILE*)m_handle );
200        fwrite( "\n", 1, 1, (FILE*)m_handle );
201        if ( m_flush ) fflush( (FILE*)m_handle );
202#endif
[4]203}
204
[365]205nv::log_file_sink::log_file_sink( const string_ref& file_name, bool flush_always /*= true */ )
206        : log_handle_sink( nullptr, flush_always )
[4]207{
[365]208#if 0 // NV_PLATFORM == NV_WINDOWS
209        // See comments in log_handle_sink
210        HANDLE handle = CreateFile( file_name.data(), GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr );
211        if ( INVALID_HANDLE_VALUE == handle )
[4]212        {
[365]213                NV_ASSERT( false, "invalid log handle" );
[4]214        }
[365]215        m_handle = handle;
216#else
217        m_handle = fopen( file_name.data(), "w" );
218#endif
[4]219}
220
[365]221nv::log_file_sink::~log_file_sink()
[4]222{
[365]223#if 0 // NV_PLATFORM == NV_WINDOWS
224        // See comments in log_handle_sink
225        CloseHandle( m_handle );
226#else
227        fclose( (FILE*) m_handle );
228#endif
[4]229}
[48]230
[365]231
[48]232nv::log_console_sink::log_console_sink( bool coloring )
233        : m_color( coloring )
234{
235#if NV_PLATFORM == NV_WINDOWS
236        m_handle = GetStdHandle( STD_OUTPUT_HANDLE );
[204]237#else
238  NV_UNUSED( m_handle );
[48]239#endif
240}
241
[368]242size_t nv::log_sink::timestamp( char* buffer ) const
[48]243{
[365]244        uint32 ms = get_system_ms();
245        unsigned int secs = (unsigned int)(ms / 1000);
246        unsigned int mm   = (unsigned int)( ms * 100 / 1000 ) % 100;
[48]247        unsigned int h    = (unsigned int)(secs / (60*60));
248        unsigned int m    = (unsigned int)(secs / 60) % 60;
249        unsigned int s    = secs % 60;
250#if NV_PLATFORM == NV_WINDOWS
[365]251        sprintf_s( buffer, 16, "%02d:%02d:%02d.%02d", h, m, s, mm );
[48]252#else
[365]253        snprintf( buffer, 16, "%02d:%02d:%02d.%02d", h, m, s, mm );
[48]254#endif
[368]255        return 11;
[48]256}
[295]257
[365]258string_ref nv::log_sink::level_name( log_level level ) const
[295]259{
260        return NV_LOG_LEVEL_NAME( level );
261}
262
[365]263string_ref nv::log_sink::padded_level_name( log_level level ) const
[295]264{
[365]265        return string_ref( NV_LOG_LEVEL_NAME_PAD( level ), 8 );
[295]266}
[365]267
Note: See TracBrowser for help on using the repository browser.