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

Last change on this file since 539 was 534, checked in by epyon, 8 years ago

CONTINUED:

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