Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in / Register
Toggle navigation
S
spdlog
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Packages
Packages
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
submodule
spdlog
Commits
0c276bea
Commit
0c276bea
authored
Mar 27, 2017
by
gabime
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
astyle
parent
5a8cecdf
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
709 additions
and
617 deletions
+709
-617
async_logger.h
include/spdlog/async_logger.h
+3
-3
async_log_helper.h
include/spdlog/details/async_log_helper.h
+253
-238
async_logger_impl.h
include/spdlog/details/async_logger_impl.h
+4
-4
format.h
include/spdlog/fmt/bundled/format.h
+0
-0
ostream.h
include/spdlog/fmt/bundled/ostream.h
+57
-46
posix.h
include/spdlog/fmt/bundled/posix.h
+275
-218
time.h
include/spdlog/fmt/bundled/time.h
+36
-31
logger.h
include/spdlog/logger.h
+4
-4
errors.cpp
tests/errors.cpp
+73
-69
utils.cpp
tests/utils.cpp
+4
-4
No files found.
include/spdlog/async_logger.h
View file @
0c276bea
...
...
@@ -64,9 +64,9 @@ public:
//Warning: this can potentialy last forever as we wait it to complete
void
flush
()
override
;
// Error handler
virtual
void
set_error_handler
(
log_err_handler
)
override
;
virtual
log_err_handler
error_handler
()
override
;
// Error handler
virtual
void
set_error_handler
(
log_err_handler
)
override
;
virtual
log_err_handler
error_handler
()
override
;
protected
:
void
_sink_it
(
details
::
log_msg
&
msg
)
override
;
...
...
include/spdlog/details/async_log_helper.h
View file @
0c276bea
...
...
@@ -32,244 +32,252 @@
namespace
spdlog
{
namespace
details
{
class
async_log_helper
{
// Async msg to move to/from the queue
// Movable only. should never be copied
enum
class
async_msg_type
{
log
,
flush
,
terminate
};
struct
async_msg
{
std
::
string
logger_name
;
level
::
level_enum
level
;
log_clock
::
time_point
time
;
size_t
thread_id
;
std
::
string
txt
;
async_msg_type
msg_type
;
async_msg
()
=
default
;
~
async_msg
()
=
default
;
async_msg
(
async_msg
&&
other
)
SPDLOG_NOEXCEPT
:
logger_name
(
std
::
move
(
other
.
logger_name
)),
level
(
std
::
move
(
other
.
level
)),
time
(
std
::
move
(
other
.
time
)),
txt
(
std
::
move
(
other
.
txt
)),
msg_type
(
std
::
move
(
other
.
msg_type
))
{}
async_msg
(
async_msg_type
m_type
)
:
msg_type
(
m_type
)
{}
async_msg
&
operator
=
(
async_msg
&&
other
)
SPDLOG_NOEXCEPT
{
logger_name
=
std
::
move
(
other
.
logger_name
);
level
=
other
.
level
;
time
=
std
::
move
(
other
.
time
);
thread_id
=
other
.
thread_id
;
txt
=
std
::
move
(
other
.
txt
);
msg_type
=
other
.
msg_type
;
return
*
this
;
}
// never copy or assign. should only be moved..
async_msg
(
const
async_msg
&
)
=
delete
;
async_msg
&
operator
=
(
const
async_msg
&
other
)
=
delete
;
// construct from log_msg
async_msg
(
const
details
::
log_msg
&
m
)
:
level
(
m
.
level
),
time
(
m
.
time
),
thread_id
(
m
.
thread_id
),
txt
(
m
.
raw
.
data
(),
m
.
raw
.
size
()),
msg_type
(
async_msg_type
::
log
)
{
namespace
details
{
class
async_log_helper
{
// Async msg to move to/from the queue
// Movable only. should never be copied
enum
class
async_msg_type
{
log
,
flush
,
terminate
};
struct
async_msg
{
std
::
string
logger_name
;
level
::
level_enum
level
;
log_clock
::
time_point
time
;
size_t
thread_id
;
std
::
string
txt
;
async_msg_type
msg_type
;
async_msg
()
=
default
;
~
async_msg
()
=
default
;
async_msg
(
async_msg
&&
other
)
SPDLOG_NOEXCEPT
:
logger_name
(
std
::
move
(
other
.
logger_name
)),
level
(
std
::
move
(
other
.
level
)),
time
(
std
::
move
(
other
.
time
)),
txt
(
std
::
move
(
other
.
txt
)),
msg_type
(
std
::
move
(
other
.
msg_type
))
{}
async_msg
(
async_msg_type
m_type
)
:
msg_type
(
m_type
)
{}
async_msg
&
operator
=
(
async_msg
&&
other
)
SPDLOG_NOEXCEPT
{
logger_name
=
std
::
move
(
other
.
logger_name
);
level
=
other
.
level
;
time
=
std
::
move
(
other
.
time
);
thread_id
=
other
.
thread_id
;
txt
=
std
::
move
(
other
.
txt
);
msg_type
=
other
.
msg_type
;
return
*
this
;
}
// never copy or assign. should only be moved..
async_msg
(
const
async_msg
&
)
=
delete
;
async_msg
&
operator
=
(
const
async_msg
&
other
)
=
delete
;
// construct from log_msg
async_msg
(
const
details
::
log_msg
&
m
)
:
level
(
m
.
level
),
time
(
m
.
time
),
thread_id
(
m
.
thread_id
),
txt
(
m
.
raw
.
data
(),
m
.
raw
.
size
()),
msg_type
(
async_msg_type
::
log
)
{
#ifndef SPDLOG_NO_NAME
logger_name
=
*
m
.
logger_name
;
logger_name
=
*
m
.
logger_name
;
#endif
}
}
// copy into log_msg
void
fill_log_msg
(
log_msg
&
msg
)
{
msg
.
logger_name
=
&
logger_name
;
msg
.
level
=
level
;
msg
.
time
=
time
;
msg
.
thread_id
=
thread_id
;
msg
.
raw
<<
txt
;
}
};
// copy into log_msg
void
fill_log_msg
(
log_msg
&
msg
)
{
msg
.
logger_name
=
&
logger_name
;
msg
.
level
=
level
;
msg
.
time
=
time
;
msg
.
thread_id
=
thread_id
;
msg
.
raw
<<
txt
;
}
};
public
:
public
:
using
item_type
=
async_msg
;
using
q_type
=
details
::
mpmc_bounded_queue
<
item_type
>
;
using
item_type
=
async_msg
;
using
q_type
=
details
::
mpmc_bounded_queue
<
item_type
>
;
using
clock
=
std
::
chrono
::
steady_clock
;
using
clock
=
std
::
chrono
::
steady_clock
;
async_log_helper
(
formatter_ptr
formatter
,
const
std
::
vector
<
sink_ptr
>&
sinks
,
size_t
queue_size
,
const
log_err_handler
err_handler
,
const
async_overflow_policy
overflow_policy
=
async_overflow_policy
::
block_retry
,
const
std
::
function
<
void
()
>&
worker_warmup_cb
=
nullptr
,
const
std
::
chrono
::
milliseconds
&
flush_interval_ms
=
std
::
chrono
::
milliseconds
::
zero
(),
const
std
::
function
<
void
()
>&
worker_teardown_cb
=
nullptr
);
async_log_helper
(
formatter_ptr
formatter
,
const
std
::
vector
<
sink_ptr
>&
sinks
,
size_t
queue_size
,
const
log_err_handler
err_handler
,
const
async_overflow_policy
overflow_policy
=
async_overflow_policy
::
block_retry
,
const
std
::
function
<
void
()
>&
worker_warmup_cb
=
nullptr
,
const
std
::
chrono
::
milliseconds
&
flush_interval_ms
=
std
::
chrono
::
milliseconds
::
zero
(),
const
std
::
function
<
void
()
>&
worker_teardown_cb
=
nullptr
);
void
log
(
const
details
::
log_msg
&
msg
);
void
log
(
const
details
::
log_msg
&
msg
);
// stop logging and join the back thread
~
async_log_helper
();
// stop logging and join the back thread
~
async_log_helper
();
void
set_formatter
(
formatter_ptr
);
void
set_formatter
(
formatter_ptr
);
void
flush
(
bool
wait_for_q
);
void
flush
(
bool
wait_for_q
);
void
set_error_handler
(
spdlog
::
log_err_handler
err_handler
);
void
set_error_handler
(
spdlog
::
log_err_handler
err_handler
);
private
:
formatter_ptr
_formatter
;
std
::
vector
<
std
::
shared_ptr
<
sinks
::
sink
>>
_sinks
;
private
:
formatter_ptr
_formatter
;
std
::
vector
<
std
::
shared_ptr
<
sinks
::
sink
>>
_sinks
;
// queue of messages to log
q_type
_q
;
// queue of messages to log
q_type
_q
;
log_err_handler
_err_handler
;
log_err_handler
_err_handler
;
bool
_flush_requested
;
bool
_flush_requested
;
bool
_terminate_requested
;
bool
_terminate_requested
;
// overflow policy
const
async_overflow_policy
_overflow_policy
;
// overflow policy
const
async_overflow_policy
_overflow_policy
;
// worker thread warmup callback - one can set thread priority, affinity, etc
const
std
::
function
<
void
()
>
_worker_warmup_cb
;
// worker thread warmup callback - one can set thread priority, affinity, etc
const
std
::
function
<
void
()
>
_worker_warmup_cb
;
// auto periodic sink flush parameter
const
std
::
chrono
::
milliseconds
_flush_interval_ms
;
// auto periodic sink flush parameter
const
std
::
chrono
::
milliseconds
_flush_interval_ms
;
// worker thread teardown callback
const
std
::
function
<
void
()
>
_worker_teardown_cb
;
// worker thread teardown callback
const
std
::
function
<
void
()
>
_worker_teardown_cb
;
// worker thread
std
::
thread
_worker_thread
;
// worker thread
std
::
thread
_worker_thread
;
void
push_msg
(
async_msg
&&
new_msg
);
void
push_msg
(
async_msg
&&
new_msg
);
// worker thread main loop
void
worker_loop
();
// worker thread main loop
void
worker_loop
();
// pop next message from the queue and process it. will set the last_pop to the pop time
// return false if termination of the queue is required
bool
process_next_msg
(
log_clock
::
time_point
&
last_pop
,
log_clock
::
time_point
&
last_flush
);
// pop next message from the queue and process it. will set the last_pop to the pop time
// return false if termination of the queue is required
bool
process_next_msg
(
log_clock
::
time_point
&
last_pop
,
log_clock
::
time_point
&
last_flush
);
void
handle_flush_interval
(
log_clock
::
time_point
&
now
,
log_clock
::
time_point
&
last_flush
);
void
handle_flush_interval
(
log_clock
::
time_point
&
now
,
log_clock
::
time_point
&
last_flush
);
// sleep,yield or return immediatly using the time passed since last message as a hint
static
void
sleep_or_yield
(
const
spdlog
::
log_clock
::
time_point
&
now
,
const
log_clock
::
time_point
&
last_op_time
);
// sleep,yield or return immediatly using the time passed since last message as a hint
static
void
sleep_or_yield
(
const
spdlog
::
log_clock
::
time_point
&
now
,
const
log_clock
::
time_point
&
last_op_time
);
// wait until the queue is empty
void
wait_empty_q
();
// wait until the queue is empty
void
wait_empty_q
();
};
}
};
}
}
///////////////////////////////////////////////////////////////////////////////
// async_sink class implementation
///////////////////////////////////////////////////////////////////////////////
inline
spdlog
::
details
::
async_log_helper
::
async_log_helper
(
formatter_ptr
formatter
,
const
std
::
vector
<
sink_ptr
>&
sinks
,
size_t
queue_size
,
log_err_handler
err_handler
,
const
async_overflow_policy
overflow_policy
,
const
std
::
function
<
void
()
>&
worker_warmup_cb
,
const
std
::
chrono
::
milliseconds
&
flush_interval_ms
,
const
std
::
function
<
void
()
>&
worker_teardown_cb
)
:
_formatter
(
formatter
),
_sinks
(
sinks
),
_q
(
queue_size
),
_err_handler
(
err_handler
),
_flush_requested
(
false
),
_terminate_requested
(
false
),
_overflow_policy
(
overflow_policy
),
_worker_warmup_cb
(
worker_warmup_cb
),
_flush_interval_ms
(
flush_interval_ms
),
_worker_teardown_cb
(
worker_teardown_cb
),
_worker_thread
(
&
async_log_helper
::
worker_loop
,
this
)
formatter_ptr
formatter
,
const
std
::
vector
<
sink_ptr
>&
sinks
,
size_t
queue_size
,
log_err_handler
err_handler
,
const
async_overflow_policy
overflow_policy
,
const
std
::
function
<
void
()
>&
worker_warmup_cb
,
const
std
::
chrono
::
milliseconds
&
flush_interval_ms
,
const
std
::
function
<
void
()
>&
worker_teardown_cb
)
:
_formatter
(
formatter
),
_sinks
(
sinks
),
_q
(
queue_size
),
_err_handler
(
err_handler
),
_flush_requested
(
false
),
_terminate_requested
(
false
),
_overflow_policy
(
overflow_policy
),
_worker_warmup_cb
(
worker_warmup_cb
),
_flush_interval_ms
(
flush_interval_ms
),
_worker_teardown_cb
(
worker_teardown_cb
),
_worker_thread
(
&
async_log_helper
::
worker_loop
,
this
)
{}
// Send to the worker thread termination message(level=off)
// and wait for it to finish gracefully
inline
spdlog
::
details
::
async_log_helper
::~
async_log_helper
()
{
try
{
push_msg
(
async_msg
(
async_msg_type
::
terminate
));
_worker_thread
.
join
();
}
catch
(...)
// don't crash in destructor
{
}
try
{
push_msg
(
async_msg
(
async_msg_type
::
terminate
));
_worker_thread
.
join
();
}
catch
(...)
// don't crash in destructor
{
}
}
//Try to push and block until succeeded (if the policy is not to discard when the queue is full)
inline
void
spdlog
::
details
::
async_log_helper
::
log
(
const
details
::
log_msg
&
msg
)
{
push_msg
(
async_msg
(
msg
));
push_msg
(
async_msg
(
msg
));
}
inline
void
spdlog
::
details
::
async_log_helper
::
push_msg
(
details
::
async_log_helper
::
async_msg
&&
new_msg
)
{
if
(
!
_q
.
enqueue
(
std
::
move
(
new_msg
))
&&
_overflow_policy
!=
async_overflow_policy
::
discard_log_msg
)
{
auto
last_op_time
=
details
::
os
::
now
();
auto
now
=
last_op_time
;
do
{
now
=
details
::
os
::
now
();
sleep_or_yield
(
now
,
last_op_time
);
}
while
(
!
_q
.
enqueue
(
std
::
move
(
new_msg
)));
}
if
(
!
_q
.
enqueue
(
std
::
move
(
new_msg
))
&&
_overflow_policy
!=
async_overflow_policy
::
discard_log_msg
)
{
auto
last_op_time
=
details
::
os
::
now
();
auto
now
=
last_op_time
;
do
{
now
=
details
::
os
::
now
();
sleep_or_yield
(
now
,
last_op_time
);
}
while
(
!
_q
.
enqueue
(
std
::
move
(
new_msg
)));
}
}
// optionally wait for the queue be empty and request flush from the sinks
inline
void
spdlog
::
details
::
async_log_helper
::
flush
(
bool
wait_for_q
)
{
push_msg
(
async_msg
(
async_msg_type
::
flush
));
if
(
wait_for_q
)
wait_empty_q
();
//return only make after the above flush message was processed
push_msg
(
async_msg
(
async_msg_type
::
flush
));
if
(
wait_for_q
)
wait_empty_q
();
//return only make after the above flush message was processed
}
inline
void
spdlog
::
details
::
async_log_helper
::
worker_loop
()
{
if
(
_worker_warmup_cb
)
_worker_warmup_cb
();
auto
last_pop
=
details
::
os
::
now
();
auto
last_flush
=
last_pop
;
auto
active
=
true
;
while
(
active
)
{
try
{
active
=
process_next_msg
(
last_pop
,
last_flush
);
}
catch
(
const
std
::
exception
&
ex
)
{
_err_handler
(
ex
.
what
());
}
catch
(...)
{
_err_handler
(
"Unknown exception"
);
}
}
if
(
_worker_teardown_cb
)
_worker_teardown_cb
();
if
(
_worker_warmup_cb
)
_worker_warmup_cb
();
auto
last_pop
=
details
::
os
::
now
();
auto
last_flush
=
last_pop
;
auto
active
=
true
;
while
(
active
)
{
try
{
active
=
process_next_msg
(
last_pop
,
last_flush
);
}
catch
(
const
std
::
exception
&
ex
)
{
_err_handler
(
ex
.
what
());
}
catch
(...)
{
_err_handler
(
"Unknown exception"
);
}
}
if
(
_worker_teardown_cb
)
_worker_teardown_cb
();
}
...
...
@@ -278,98 +286,105 @@ inline void spdlog::details::async_log_helper::worker_loop()
// return true if this thread should still be active (while no terminate msg was received)
inline
bool
spdlog
::
details
::
async_log_helper
::
process_next_msg
(
log_clock
::
time_point
&
last_pop
,
log_clock
::
time_point
&
last_flush
)
{
async_msg
incoming_async_msg
;
if
(
_q
.
dequeue
(
incoming_async_msg
))
{
last_pop
=
details
::
os
::
now
();
switch
(
incoming_async_msg
.
msg_type
)
{
case
async_msg_type
:
:
flush
:
_flush_requested
=
true
;
break
;
case
async_msg_type
:
:
terminate
:
_flush_requested
=
true
;
_terminate_requested
=
true
;
break
;
default
:
log_msg
incoming_log_msg
;
incoming_async_msg
.
fill_log_msg
(
incoming_log_msg
);
_formatter
->
format
(
incoming_log_msg
);
for
(
auto
&
s
:
_sinks
)
{
if
(
s
->
should_log
(
incoming_log_msg
.
level
))
{
s
->
log
(
incoming_log_msg
);
}
}
}
return
true
;
}
// Handle empty queue..
// This is the only place where the queue can terminate or flush to avoid losing messages already in the queue
else
{
auto
now
=
details
::
os
::
now
();
handle_flush_interval
(
now
,
last_flush
);
sleep_or_yield
(
now
,
last_pop
);
return
!
_terminate_requested
;
}
async_msg
incoming_async_msg
;
if
(
_q
.
dequeue
(
incoming_async_msg
))
{
last_pop
=
details
::
os
::
now
();
switch
(
incoming_async_msg
.
msg_type
)
{
case
async_msg_type
:
:
flush
:
_flush_requested
=
true
;
break
;
case
async_msg_type
:
:
terminate
:
_flush_requested
=
true
;
_terminate_requested
=
true
;
break
;
default
:
log_msg
incoming_log_msg
;
incoming_async_msg
.
fill_log_msg
(
incoming_log_msg
);
_formatter
->
format
(
incoming_log_msg
);
for
(
auto
&
s
:
_sinks
)
{
if
(
s
->
should_log
(
incoming_log_msg
.
level
))
{
s
->
log
(
incoming_log_msg
);
}
}
}
return
true
;
}
// Handle empty queue..
// This is the only place where the queue can terminate or flush to avoid losing messages already in the queue
else
{
auto
now
=
details
::
os
::
now
();
handle_flush_interval
(
now
,
last_flush
);
sleep_or_yield
(
now
,
last_pop
);
return
!
_terminate_requested
;
}
}
// flush all sinks if _flush_interval_ms has expired
inline
void
spdlog
::
details
::
async_log_helper
::
handle_flush_interval
(
log_clock
::
time_point
&
now
,
log_clock
::
time_point
&
last_flush
)
{
auto
should_flush
=
_flush_requested
||
(
_flush_interval_ms
!=
std
::
chrono
::
milliseconds
::
zero
()
&&
now
-
last_flush
>=
_flush_interval_ms
);
if
(
should_flush
)
{
for
(
auto
&
s
:
_sinks
)
s
->
flush
();
now
=
last_flush
=
details
::
os
::
now
();
_flush_requested
=
false
;
}
auto
should_flush
=
_flush_requested
||
(
_flush_interval_ms
!=
std
::
chrono
::
milliseconds
::
zero
()
&&
now
-
last_flush
>=
_flush_interval_ms
);
if
(
should_flush
)
{
for
(
auto
&
s
:
_sinks
)
s
->
flush
();
now
=
last_flush
=
details
::
os
::
now
();
_flush_requested
=
false
;
}
}
inline
void
spdlog
::
details
::
async_log_helper
::
set_formatter
(
formatter_ptr
msg_formatter
)
{
_formatter
=
msg_formatter
;
_formatter
=
msg_formatter
;
}
// spin, yield or sleep. use the time passed since last message as a hint
inline
void
spdlog
::
details
::
async_log_helper
::
sleep_or_yield
(
const
spdlog
::
log_clock
::
time_point
&
now
,
const
spdlog
::
log_clock
::
time_point
&
last_op_time
)
{
using
namespace
std
::
this_thread
;
using
std
::
chrono
::
milliseconds
;
using
std
::
chrono
::
microseconds
;
using
namespace
std
::
this_thread
;
using
std
::
chrono
::
milliseconds
;
using
std
::
chrono
::
microseconds
;
auto
time_since_op
=
now
-
last_op_time
;
auto
time_since_op
=
now
-
last_op_time
;
// spin upto 50 micros
if
(
time_since_op
<=
microseconds
(
50
))
return
;
// spin upto 50 micros
if
(
time_since_op
<=
microseconds
(
50
))
return
;
// yield upto 150 micros
if
(
time_since_op
<=
microseconds
(
100
))
return
std
::
this_thread
::
yield
();
// yield upto 150 micros
if
(
time_since_op
<=
microseconds
(
100
))
return
std
::
this_thread
::
yield
();
// sleep for 20 ms upto 200 ms
if
(
time_since_op
<=
milliseconds
(
200
))
return
sleep_for
(
milliseconds
(
20
));
// sleep for 20 ms upto 200 ms
if
(
time_since_op
<=
milliseconds
(
200
))
return
sleep_for
(
milliseconds
(
20
));
// sleep for 200 ms
return
sleep_for
(
milliseconds
(
200
));
// sleep for 200 ms
return
sleep_for
(
milliseconds
(
200
));
}
// wait for the queue to be empty
inline
void
spdlog
::
details
::
async_log_helper
::
wait_empty_q
()
{
auto
last_op
=
details
::
os
::
now
();
while
(
_q
.
approx_size
()
>
0
)
{
sleep_or_yield
(
details
::
os
::
now
(),
last_op
);
}
auto
last_op
=
details
::
os
::
now
();
while
(
_q
.
approx_size
()
>
0
)
{
sleep_or_yield
(
details
::
os
::
now
(),
last_op
);
}
}
inline
void
spdlog
::
details
::
async_log_helper
::
set_error_handler
(
spdlog
::
log_err_handler
err_handler
)
{
_err_handler
=
err_handler
;
_err_handler
=
err_handler
;
}
...
...
include/spdlog/details/async_logger_impl.h
View file @
0c276bea
...
...
@@ -60,13 +60,13 @@ inline void spdlog::async_logger::flush()
// Error handler
inline
void
spdlog
::
async_logger
::
set_error_handler
(
spdlog
::
log_err_handler
err_handler
)
{
_err_handler
=
err_handler
;
_async_log_helper
->
set_error_handler
(
err_handler
);
_err_handler
=
err_handler
;
_async_log_helper
->
set_error_handler
(
err_handler
);
}
inline
spdlog
::
log_err_handler
spdlog
::
async_logger
::
error_handler
()
{
return
_err_handler
;
return
_err_handler
;
}
...
...
include/spdlog/fmt/bundled/format.h
View file @
0c276bea
This source diff could not be displayed because it is too large. You can
view the blob
instead.
include/spdlog/fmt/bundled/ostream.h
View file @
0c276bea
...
...
@@ -13,74 +13,85 @@
#include "format.h"
#include <ostream>
namespace
fmt
{
namespace
fmt
{
namespace
internal
{
namespace
internal
{
template
<
class
Char
>
class
FormatBuf
:
public
std
::
basic_streambuf
<
Char
>
{
private
:
typedef
typename
std
::
basic_streambuf
<
Char
>::
int_type
int_type
;
typedef
typename
std
::
basic_streambuf
<
Char
>::
traits_type
traits_type
;
Buffer
<
Char
>
&
buffer_
;
Char
*
start_
;
public
:
FormatBuf
(
Buffer
<
Char
>
&
buffer
)
:
buffer_
(
buffer
),
start_
(
&
buffer
[
0
])
{
this
->
setp
(
start_
,
start_
+
buffer_
.
capacity
());
}
int_type
overflow
(
int_type
ch
=
traits_type
::
eof
())
{
if
(
!
traits_type
::
eq_int_type
(
ch
,
traits_type
::
eof
()))
{
size_t
buf_size
=
size
();
buffer_
.
resize
(
buf_size
);
buffer_
.
reserve
(
buf_size
*
2
);
start_
=
&
buffer_
[
0
];
start_
[
buf_size
]
=
traits_type
::
to_char_type
(
ch
);
this
->
setp
(
start_
+
buf_size
+
1
,
start_
+
buf_size
*
2
);
class
FormatBuf
:
public
std
::
basic_streambuf
<
Char
>
{
private
:
typedef
typename
std
::
basic_streambuf
<
Char
>::
int_type
int_type
;
typedef
typename
std
::
basic_streambuf
<
Char
>::
traits_type
traits_type
;
Buffer
<
Char
>
&
buffer_
;
Char
*
start_
;
public
:
FormatBuf
(
Buffer
<
Char
>
&
buffer
)
:
buffer_
(
buffer
),
start_
(
&
buffer
[
0
])
{
this
->
setp
(
start_
,
start_
+
buffer_
.
capacity
());
}
return
ch
;
}
size_t
size
()
const
{
return
to_unsigned
(
this
->
pptr
()
-
start_
);
}
int_type
overflow
(
int_type
ch
=
traits_type
::
eof
())
{
if
(
!
traits_type
::
eq_int_type
(
ch
,
traits_type
::
eof
()))
{
size_t
buf_size
=
size
();
buffer_
.
resize
(
buf_size
);
buffer_
.
reserve
(
buf_size
*
2
);
start_
=
&
buffer_
[
0
];
start_
[
buf_size
]
=
traits_type
::
to_char_type
(
ch
);
this
->
setp
(
start_
+
buf_size
+
1
,
start_
+
buf_size
*
2
);
}
return
ch
;
}
size_t
size
()
const
{
return
to_unsigned
(
this
->
pptr
()
-
start_
);
}
};
Yes
&
convert
(
std
::
ostream
&
);
struct
DummyStream
:
std
::
ostream
{
DummyStream
();
// Suppress a bogus warning in MSVC.
// Hide all operator<< overloads from std::ostream.
void
operator
<<
(
Null
<>
);
struct
DummyStream
:
std
::
ostream
{
DummyStream
();
// Suppress a bogus warning in MSVC.
// Hide all operator<< overloads from std::ostream.
void
operator
<<
(
Null
<>
);
};
No
&
operator
<<
(
std
::
ostream
&
,
int
);
template
<
typename
T
>
struct
ConvertToIntImpl
<
T
,
true
>
{
// Convert to int only if T doesn't have an overloaded operator<<.
enum
{
value
=
sizeof
(
convert
(
get
<
DummyStream
>
()
<<
get
<
T
>
()))
==
sizeof
(
No
)
};
struct
ConvertToIntImpl
<
T
,
true
>
{
// Convert to int only if T doesn't have an overloaded operator<<.
enum
{
value
=
sizeof
(
convert
(
get
<
DummyStream
>
()
<<
get
<
T
>
()))
==
sizeof
(
No
)
};
};
}
// namespace internal
// Formats a value.
template
<
typename
Char
,
typename
ArgFormatter
,
typename
T
>
void
format
(
BasicFormatter
<
Char
,
ArgFormatter
>
&
f
,
const
Char
*&
format_str
,
const
T
&
value
)
{
internal
::
MemoryBuffer
<
Char
,
internal
::
INLINE_BUFFER_SIZE
>
buffer
;
const
Char
*&
format_str
,
const
T
&
value
)
{
internal
::
MemoryBuffer
<
Char
,
internal
::
INLINE_BUFFER_SIZE
>
buffer
;
internal
::
FormatBuf
<
Char
>
format_buf
(
buffer
);
std
::
basic_ostream
<
Char
>
output
(
&
format_buf
);
output
<<
value
;
internal
::
FormatBuf
<
Char
>
format_buf
(
buffer
);
std
::
basic_ostream
<
Char
>
output
(
&
format_buf
);
output
<<
value
;
BasicStringRef
<
Char
>
str
(
&
buffer
[
0
],
format_buf
.
size
());
typedef
internal
::
MakeArg
<
BasicFormatter
<
Char
>
>
MakeArg
;
format_str
=
f
.
format
(
format_str
,
MakeArg
(
str
));
BasicStringRef
<
Char
>
str
(
&
buffer
[
0
],
format_buf
.
size
());
typedef
internal
::
MakeArg
<
BasicFormatter
<
Char
>
>
MakeArg
;
format_str
=
f
.
format
(
format_str
,
MakeArg
(
str
));
}
/**
...
...
include/spdlog/fmt/bundled/posix.h
View file @
0c276bea
...
...
@@ -83,112 +83,134 @@
#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
namespace
fmt
{
namespace
fmt
{
// An error code.
class
ErrorCode
{
private
:
int
value_
;
class
ErrorCode
{
private
:
int
value_
;
public
:
explicit
ErrorCode
(
int
value
=
0
)
FMT_NOEXCEPT
:
value_
(
value
)
{}
public
:
explicit
ErrorCode
(
int
value
=
0
)
FMT_NOEXCEPT
:
value_
(
value
)
{}
int
get
()
const
FMT_NOEXCEPT
{
return
value_
;
}
int
get
()
const
FMT_NOEXCEPT
{
return
value_
;
}
};
// A buffered file.
class
BufferedFile
{
private
:
FILE
*
file_
;
class
BufferedFile
{
private
:
FILE
*
file_
;
friend
class
File
;
friend
class
File
;
explicit
BufferedFile
(
FILE
*
f
)
:
file_
(
f
)
{}
explicit
BufferedFile
(
FILE
*
f
)
:
file_
(
f
)
{}
public
:
// Constructs a BufferedFile object which doesn't represent any file.
BufferedFile
()
FMT_NOEXCEPT
:
file_
(
0
)
{}
public
:
// Constructs a BufferedFile object which doesn't represent any file.
BufferedFile
()
FMT_NOEXCEPT
:
file_
(
0
)
{}
// Destroys the object closing the file it represents if any.
~
BufferedFile
()
FMT_NOEXCEPT
;
// Destroys the object closing the file it represents if any.
~
BufferedFile
()
FMT_NOEXCEPT
;
#if !FMT_USE_RVALUE_REFERENCES
// Emulate a move constructor and a move assignment operator if rvalue
// references are not supported.
// Emulate a move constructor and a move assignment operator if rvalue
// references are not supported.
private
:
// A proxy object to emulate a move constructor.
// It is private to make it impossible call operator Proxy directly.
struct
Proxy
{
FILE
*
file
;
};
private
:
// A proxy object to emulate a move constructor.
// It is private to make it impossible call operator Proxy directly.
struct
Proxy
{
FILE
*
file
;
};
public
:
// A "move constructor" for moving from a temporary.
BufferedFile
(
Proxy
p
)
FMT_NOEXCEPT
:
file_
(
p
.
file
)
{}
// A "move constructor" for moving from an lvalue.
BufferedFile
(
BufferedFile
&
f
)
FMT_NOEXCEPT
:
file_
(
f
.
file_
)
{
f
.
file_
=
0
;
}
// A "move assignment operator" for moving from a temporary.
BufferedFile
&
operator
=
(
Proxy
p
)
{
close
();
file_
=
p
.
file
;
return
*
this
;
}
// A "move assignment operator" for moving from an lvalue.
BufferedFile
&
operator
=
(
BufferedFile
&
other
)
{
close
();
file_
=
other
.
file_
;
other
.
file_
=
0
;
return
*
this
;
}
// Returns a proxy object for moving from a temporary:
// BufferedFile file = BufferedFile(...);
operator
Proxy
()
FMT_NOEXCEPT
{
Proxy
p
=
{
file_
};
file_
=
0
;
return
p
;
}
// A "move constructor" for moving from a temporary.
BufferedFile
(
Proxy
p
)
FMT_NOEXCEPT
:
file_
(
p
.
file
)
{}
// A "move constructor" for moving from an lvalue.
BufferedFile
(
BufferedFile
&
f
)
FMT_NOEXCEPT
:
file_
(
f
.
file_
)
{
f
.
file_
=
0
;
}
// A "move assignment operator" for moving from a temporary.
BufferedFile
&
operator
=
(
Proxy
p
)
{
close
();
file_
=
p
.
file
;
return
*
this
;
}
// A "move assignment operator" for moving from an lvalue.
BufferedFile
&
operator
=
(
BufferedFile
&
other
)
{
close
();
file_
=
other
.
file_
;
other
.
file_
=
0
;
return
*
this
;
}
// Returns a proxy object for moving from a temporary:
// BufferedFile file = BufferedFile(...);
operator
Proxy
()
FMT_NOEXCEPT
{
Proxy
p
=
{
file_
};
file_
=
0
;
return
p
;
}
#else
private
:
FMT_DISALLOW_COPY_AND_ASSIGN
(
BufferedFile
);
public
:
BufferedFile
(
BufferedFile
&&
other
)
FMT_NOEXCEPT
:
file_
(
other
.
file_
)
{
other
.
file_
=
0
;
}
BufferedFile
&
operator
=
(
BufferedFile
&&
other
)
{
close
();
file_
=
other
.
file_
;
other
.
file_
=
0
;
return
*
this
;
}
private
:
FMT_DISALLOW_COPY_AND_ASSIGN
(
BufferedFile
);
public
:
BufferedFile
(
BufferedFile
&&
other
)
FMT_NOEXCEPT
:
file_
(
other
.
file_
)
{
other
.
file_
=
0
;
}
BufferedFile
&
operator
=
(
BufferedFile
&&
other
)
{
close
();
file_
=
other
.
file_
;
other
.
file_
=
0
;
return
*
this
;
}
#endif
// Opens a file.
BufferedFile
(
CStringRef
filename
,
CStringRef
mode
);
// Opens a file.
BufferedFile
(
CStringRef
filename
,
CStringRef
mode
);
// Closes the file.
void
close
();
// Closes the file.
void
close
();
// Returns the pointer to a FILE object representing this file.
FILE
*
get
()
const
FMT_NOEXCEPT
{
return
file_
;
}
// Returns the pointer to a FILE object representing this file.
FILE
*
get
()
const
FMT_NOEXCEPT
{
return
file_
;
}
// We place parentheses around fileno to workaround a bug in some versions
// of MinGW that define fileno as a macro.
int
(
fileno
)()
const
;
// We place parentheses around fileno to workaround a bug in some versions
// of MinGW that define fileno as a macro.
int
(
fileno
)()
const
;
void
print
(
CStringRef
format_str
,
const
ArgList
&
args
)
{
fmt
::
print
(
file_
,
format_str
,
args
);
}
FMT_VARIADIC
(
void
,
print
,
CStringRef
)
void
print
(
CStringRef
format_str
,
const
ArgList
&
args
)
{
fmt
::
print
(
file_
,
format_str
,
args
);
}
FMT_VARIADIC
(
void
,
print
,
CStringRef
)
};
// A file. Closed file is represented by a File object with descriptor -1.
...
...
@@ -197,125 +219,141 @@ public:
// closing the file multiple times will cause a crash on Windows rather
// than an exception. You can get standard behavior by overriding the
// invalid parameter handler with _set_invalid_parameter_handler.
class
File
{
private
:
int
fd_
;
// File descriptor.
class
File
{
private
:
int
fd_
;
// File descriptor.
// Constructs a File object with a given descriptor.
explicit
File
(
int
fd
)
:
fd_
(
fd
)
{}
// Constructs a File object with a given descriptor.
explicit
File
(
int
fd
)
:
fd_
(
fd
)
{}
public
:
// Possible values for the oflag argument to the constructor.
enum
{
RDONLY
=
FMT_POSIX
(
O_RDONLY
),
// Open for reading only.
WRONLY
=
FMT_POSIX
(
O_WRONLY
),
// Open for writing only.
RDWR
=
FMT_POSIX
(
O_RDWR
)
// Open for reading and writing.
};
public
:
// Possible values for the oflag argument to the constructor.
enum
{
RDONLY
=
FMT_POSIX
(
O_RDONLY
),
// Open for reading only.
WRONLY
=
FMT_POSIX
(
O_WRONLY
),
// Open for writing only.
RDWR
=
FMT_POSIX
(
O_RDWR
)
// Open for reading and writing.
};
// Constructs a File object which doesn't represent any file.
File
()
FMT_NOEXCEPT
:
fd_
(
-
1
)
{}
// Constructs a File object which doesn't represent any file.
File
()
FMT_NOEXCEPT
:
fd_
(
-
1
)
{}
// Opens a file and constructs a File object representing this file.
File
(
CStringRef
path
,
int
oflag
);
// Opens a file and constructs a File object representing this file.
File
(
CStringRef
path
,
int
oflag
);
#if !FMT_USE_RVALUE_REFERENCES
// Emulate a move constructor and a move assignment operator if rvalue
// references are not supported.
private
:
// A proxy object to emulate a move constructor.
// It is private to make it impossible call operator Proxy directly.
struct
Proxy
{
int
fd
;
};
public
:
// A "move constructor" for moving from a temporary.
File
(
Proxy
p
)
FMT_NOEXCEPT
:
fd_
(
p
.
fd
)
{}
// A "move constructor" for moving from an lvalue.
File
(
File
&
other
)
FMT_NOEXCEPT
:
fd_
(
other
.
fd_
)
{
other
.
fd_
=
-
1
;
}
// A "move assignment operator" for moving from a temporary.
File
&
operator
=
(
Proxy
p
)
{
close
();
fd_
=
p
.
fd
;
return
*
this
;
}
// A "move assignment operator" for moving from an lvalue.
File
&
operator
=
(
File
&
other
)
{
close
();
fd_
=
other
.
fd_
;
other
.
fd_
=
-
1
;
return
*
this
;
}
// Returns a proxy object for moving from a temporary:
// File file = File(...);
operator
Proxy
()
FMT_NOEXCEPT
{
Proxy
p
=
{
fd_
};
fd_
=
-
1
;
return
p
;
}
// Emulate a move constructor and a move assignment operator if rvalue
// references are not supported.
private
:
// A proxy object to emulate a move constructor.
// It is private to make it impossible call operator Proxy directly.
struct
Proxy
{
int
fd
;
};
public
:
// A "move constructor" for moving from a temporary.
File
(
Proxy
p
)
FMT_NOEXCEPT
:
fd_
(
p
.
fd
)
{}
// A "move constructor" for moving from an lvalue.
File
(
File
&
other
)
FMT_NOEXCEPT
:
fd_
(
other
.
fd_
)
{
other
.
fd_
=
-
1
;
}
// A "move assignment operator" for moving from a temporary.
File
&
operator
=
(
Proxy
p
)
{
close
();
fd_
=
p
.
fd
;
return
*
this
;
}
// A "move assignment operator" for moving from an lvalue.
File
&
operator
=
(
File
&
other
)
{
close
();
fd_
=
other
.
fd_
;
other
.
fd_
=
-
1
;
return
*
this
;
}
// Returns a proxy object for moving from a temporary:
// File file = File(...);
operator
Proxy
()
FMT_NOEXCEPT
{
Proxy
p
=
{
fd_
};
fd_
=
-
1
;
return
p
;
}
#else
private
:
FMT_DISALLOW_COPY_AND_ASSIGN
(
File
);
public
:
File
(
File
&&
other
)
FMT_NOEXCEPT
:
fd_
(
other
.
fd_
)
{
other
.
fd_
=
-
1
;
}
File
&
operator
=
(
File
&&
other
)
{
close
();
fd_
=
other
.
fd_
;
other
.
fd_
=
-
1
;
return
*
this
;
}
private
:
FMT_DISALLOW_COPY_AND_ASSIGN
(
File
);
public
:
File
(
File
&&
other
)
FMT_NOEXCEPT
:
fd_
(
other
.
fd_
)
{
other
.
fd_
=
-
1
;
}
File
&
operator
=
(
File
&&
other
)
{
close
();
fd_
=
other
.
fd_
;
other
.
fd_
=
-
1
;
return
*
this
;
}
#endif
// Destroys the object closing the file it represents if any.
~
File
()
FMT_NOEXCEPT
;
// Destroys the object closing the file it represents if any.
~
File
()
FMT_NOEXCEPT
;
// Returns the file descriptor.
int
descriptor
()
const
FMT_NOEXCEPT
{
return
fd_
;
}
// Returns the file descriptor.
int
descriptor
()
const
FMT_NOEXCEPT
{
return
fd_
;
}
// Closes the file.
void
close
();
// Closes the file.
void
close
();
// Returns the file size. The size has signed type for consistency with
// stat::st_size.
LongLong
size
()
const
;
// Returns the file size. The size has signed type for consistency with
// stat::st_size.
LongLong
size
()
const
;
// Attempts to read count bytes from the file into the specified buffer.
std
::
size_t
read
(
void
*
buffer
,
std
::
size_t
count
);
// Attempts to read count bytes from the file into the specified buffer.
std
::
size_t
read
(
void
*
buffer
,
std
::
size_t
count
);
// Attempts to write count bytes from the specified buffer to the file.
std
::
size_t
write
(
const
void
*
buffer
,
std
::
size_t
count
);
// Attempts to write count bytes from the specified buffer to the file.
std
::
size_t
write
(
const
void
*
buffer
,
std
::
size_t
count
);
// Duplicates a file descriptor with the dup function and returns
// the duplicate as a file object.
static
File
dup
(
int
fd
);
// Duplicates a file descriptor with the dup function and returns
// the duplicate as a file object.
static
File
dup
(
int
fd
);
// Makes fd be the copy of this file descriptor, closing fd first if
// necessary.
void
dup2
(
int
fd
);
// Makes fd be the copy of this file descriptor, closing fd first if
// necessary.
void
dup2
(
int
fd
);
// Makes fd be the copy of this file descriptor, closing fd first if
// necessary.
void
dup2
(
int
fd
,
ErrorCode
&
ec
)
FMT_NOEXCEPT
;
// Makes fd be the copy of this file descriptor, closing fd first if
// necessary.
void
dup2
(
int
fd
,
ErrorCode
&
ec
)
FMT_NOEXCEPT
;
// Creates a pipe setting up read_end and write_end file objects for reading
// and writing respectively.
static
void
pipe
(
File
&
read_end
,
File
&
write_end
);
// Creates a pipe setting up read_end and write_end file objects for reading
// and writing respectively.
static
void
pipe
(
File
&
read_end
,
File
&
write_end
);
// Creates a BufferedFile object associated with this file and detaches
// this File object from the file.
BufferedFile
fdopen
(
const
char
*
mode
);
// Creates a BufferedFile object associated with this file and detaches
// this File object from the file.
BufferedFile
fdopen
(
const
char
*
mode
);
};
// Returns the memory page size.
...
...
@@ -328,58 +366,77 @@ long getpagesize();
#ifdef FMT_LOCALE
// A "C" numeric locale.
class
Locale
{
private
:
class
Locale
{
private
:
# ifdef _MSC_VER
typedef
_locale_t
locale_t
;
typedef
_locale_t
locale_t
;
enum
{
LC_NUMERIC_MASK
=
LC_NUMERIC
};
enum
{
LC_NUMERIC_MASK
=
LC_NUMERIC
};
static
locale_t
newlocale
(
int
category_mask
,
const
char
*
locale
,
locale_t
)
{
return
_create_locale
(
category_mask
,
locale
);
}
static
locale_t
newlocale
(
int
category_mask
,
const
char
*
locale
,
locale_t
)
{
return
_create_locale
(
category_mask
,
locale
);
}
static
void
freelocale
(
locale_t
locale
)
{
_free_locale
(
locale
);
}
static
void
freelocale
(
locale_t
locale
)
{
_free_locale
(
locale
);
}
static
double
strtod_l
(
const
char
*
nptr
,
char
**
endptr
,
_locale_t
locale
)
{
return
_strtod_l
(
nptr
,
endptr
,
locale
);
}
static
double
strtod_l
(
const
char
*
nptr
,
char
**
endptr
,
_locale_t
locale
)
{
return
_strtod_l
(
nptr
,
endptr
,
locale
);
}
# endif
locale_t
locale_
;
FMT_DISALLOW_COPY_AND_ASSIGN
(
Locale
);
public
:
typedef
locale_t
Type
;
locale_t
locale_
;
Locale
()
:
locale_
(
newlocale
(
LC_NUMERIC_MASK
,
"C"
,
NULL
))
{
if
(
!
locale_
)
FMT_THROW
(
fmt
::
SystemError
(
errno
,
"cannot create locale"
));
}
~
Locale
()
{
freelocale
(
locale_
);
}
FMT_DISALLOW_COPY_AND_ASSIGN
(
Locale
);
Type
get
()
const
{
return
locale_
;
}
// Converts string to floating-point number and advances str past the end
// of the parsed input.
double
strtod
(
const
char
*&
str
)
const
{
char
*
end
=
0
;
double
result
=
strtod_l
(
str
,
&
end
,
locale_
);
str
=
end
;
return
result
;
}
public
:
typedef
locale_t
Type
;
Locale
()
:
locale_
(
newlocale
(
LC_NUMERIC_MASK
,
"C"
,
NULL
))
{
if
(
!
locale_
)
FMT_THROW
(
fmt
::
SystemError
(
errno
,
"cannot create locale"
));
}
~
Locale
()
{
freelocale
(
locale_
);
}
Type
get
()
const
{
return
locale_
;
}
// Converts string to floating-point number and advances str past the end
// of the parsed input.
double
strtod
(
const
char
*&
str
)
const
{
char
*
end
=
0
;
double
result
=
strtod_l
(
str
,
&
end
,
locale_
);
str
=
end
;
return
result
;
}
};
#endif // FMT_LOCALE
}
// namespace fmt
#if !FMT_USE_RVALUE_REFERENCES
namespace
std
{
namespace
std
{
// For compatibility with C++98.
inline
fmt
::
BufferedFile
&
move
(
fmt
::
BufferedFile
&
f
)
{
return
f
;
}
inline
fmt
::
File
&
move
(
fmt
::
File
&
f
)
{
return
f
;
}
inline
fmt
::
BufferedFile
&
move
(
fmt
::
BufferedFile
&
f
)
{
return
f
;
}
inline
fmt
::
File
&
move
(
fmt
::
File
&
f
)
{
return
f
;
}
}
#endif
...
...
include/spdlog/fmt/bundled/time.h
View file @
0c276bea
...
...
@@ -13,40 +13,45 @@
#include "format.h"
#include <ctime>
namespace
fmt
{
namespace
fmt
{
template
<
typename
ArgFormatter
>
void
format
(
BasicFormatter
<
char
,
ArgFormatter
>
&
f
,
const
char
*&
format_str
,
const
std
::
tm
&
tm
)
{
if
(
*
format_str
==
':'
)
++
format_str
;
const
char
*
end
=
format_str
;
while
(
*
end
&&
*
end
!=
'}'
)
++
end
;
if
(
*
end
!=
'}'
)
FMT_THROW
(
FormatError
(
"missing '}' in format string"
));
internal
::
MemoryBuffer
<
char
,
internal
::
INLINE_BUFFER_SIZE
>
format
;
format
.
append
(
format_str
,
end
+
1
);
format
[
format
.
size
()
-
1
]
=
'\0'
;
Buffer
<
char
>
&
buffer
=
f
.
writer
().
buffer
();
std
::
size_t
start
=
buffer
.
size
();
for
(;;)
{
std
::
size_t
size
=
buffer
.
capacity
()
-
start
;
std
::
size_t
count
=
std
::
strftime
(
&
buffer
[
start
],
size
,
&
format
[
0
],
&
tm
);
if
(
count
!=
0
)
{
buffer
.
resize
(
start
+
count
);
break
;
const
char
*&
format_str
,
const
std
::
tm
&
tm
)
{
if
(
*
format_str
==
':'
)
++
format_str
;
const
char
*
end
=
format_str
;
while
(
*
end
&&
*
end
!=
'}'
)
++
end
;
if
(
*
end
!=
'}'
)
FMT_THROW
(
FormatError
(
"missing '}' in format string"
));
internal
::
MemoryBuffer
<
char
,
internal
::
INLINE_BUFFER_SIZE
>
format
;
format
.
append
(
format_str
,
end
+
1
);
format
[
format
.
size
()
-
1
]
=
'\0'
;
Buffer
<
char
>
&
buffer
=
f
.
writer
().
buffer
();
std
::
size_t
start
=
buffer
.
size
();
for
(;;)
{
std
::
size_t
size
=
buffer
.
capacity
()
-
start
;
std
::
size_t
count
=
std
::
strftime
(
&
buffer
[
start
],
size
,
&
format
[
0
],
&
tm
);
if
(
count
!=
0
)
{
buffer
.
resize
(
start
+
count
);
break
;
}
if
(
size
>=
format
.
size
()
*
256
)
{
// If the buffer is 256 times larger than the format string, assume
// that `strftime` gives an empty result. There doesn't seem to be a
// better way to distinguish the two cases:
// https://github.com/fmtlib/fmt/issues/367
break
;
}
const
std
::
size_t
MIN_GROWTH
=
10
;
buffer
.
reserve
(
buffer
.
capacity
()
+
(
size
>
MIN_GROWTH
?
size
:
MIN_GROWTH
));
}
if
(
size
>=
format
.
size
()
*
256
)
{
// If the buffer is 256 times larger than the format string, assume
// that `strftime` gives an empty result. There doesn't seem to be a
// better way to distinguish the two cases:
// https://github.com/fmtlib/fmt/issues/367
break
;
}
const
std
::
size_t
MIN_GROWTH
=
10
;
buffer
.
reserve
(
buffer
.
capacity
()
+
(
size
>
MIN_GROWTH
?
size
:
MIN_GROWTH
));
}
format_str
=
end
+
1
;
format_str
=
end
+
1
;
}
}
...
...
include/spdlog/logger.h
View file @
0c276bea
...
...
@@ -58,7 +58,7 @@ public:
const
std
::
string
&
name
()
const
;
void
set_pattern
(
const
std
::
string
&
);
void
set_formatter
(
formatter_ptr
);
// automatically call flush() if message level >= log_level
void
flush_on
(
level
::
level_enum
log_level
);
...
...
@@ -66,9 +66,9 @@ public:
const
std
::
vector
<
sink_ptr
>&
sinks
()
const
;
// error handler
virtual
void
set_error_handler
(
log_err_handler
);
virtual
log_err_handler
error_handler
();
// error handler
virtual
void
set_error_handler
(
log_err_handler
);
virtual
log_err_handler
error_handler
();
protected
:
virtual
void
_sink_it
(
details
::
log_msg
&
);
...
...
tests/errors.cpp
View file @
0c276bea
...
...
@@ -8,30 +8,30 @@
class
failing_sink
:
public
spdlog
::
sinks
::
sink
{
void
log
(
const
spdlog
::
details
::
log_msg
&
msg
)
override
{
throw
std
::
runtime_error
(
"some error happened during log"
);
}
void
flush
()
{}
class
failing_sink
:
public
spdlog
::
sinks
::
sink
{
void
log
(
const
spdlog
::
details
::
log_msg
&
msg
)
override
{
throw
std
::
runtime_error
(
"some error happened during log"
);
}
void
flush
()
{}
};
TEST_CASE
(
"default_error_handler"
,
"[errors]]"
)
{
prepare_logdir
();
std
::
string
filename
=
"logs/simple_log.txt"
;
prepare_logdir
();
std
::
string
filename
=
"logs/simple_log.txt"
;
auto
logger
=
spdlog
::
create
<
spdlog
::
sinks
::
simple_file_sink_mt
>
(
"logger"
,
filename
,
true
);
logger
->
set_pattern
(
"%v"
);
logger
->
info
(
"Test message {} {}"
,
1
);
logger
->
info
(
"Test message {}"
,
2
);
logger
->
flush
();
auto
logger
=
spdlog
::
create
<
spdlog
::
sinks
::
simple_file_sink_mt
>
(
"logger"
,
filename
,
true
);
logger
->
set_pattern
(
"%v"
);
logger
->
info
(
"Test message {} {}"
,
1
);
logger
->
info
(
"Test message {}"
,
2
);
logger
->
flush
();
REQUIRE
(
file_contents
(
filename
)
==
std
::
string
(
"Test message 2
\n
"
));
REQUIRE
(
count_lines
(
filename
)
==
1
);
REQUIRE
(
file_contents
(
filename
)
==
std
::
string
(
"Test message 2
\n
"
));
REQUIRE
(
count_lines
(
filename
)
==
1
);
}
...
...
@@ -41,69 +41,73 @@ struct custom_ex
{};
TEST_CASE
(
"custom_error_handler"
,
"[errors]]"
)
{
prepare_logdir
();
std
::
string
filename
=
"logs/simple_log.txt"
;
auto
logger
=
spdlog
::
create
<
spdlog
::
sinks
::
simple_file_sink_mt
>
(
"logger"
,
filename
,
true
);
logger
->
flush_on
(
spdlog
::
level
::
info
);
logger
->
set_error_handler
([
=
](
const
std
::
string
&
msg
)
{
throw
custom_ex
();
});
logger
->
info
(
"Good message #1"
);
REQUIRE_THROWS_AS
(
logger
->
info
(
"Bad format msg {} {}"
,
"xxx"
),
custom_ex
);
logger
->
info
(
"Good message #2"
);
REQUIRE
(
count_lines
(
filename
)
==
2
);
prepare_logdir
();
std
::
string
filename
=
"logs/simple_log.txt"
;
auto
logger
=
spdlog
::
create
<
spdlog
::
sinks
::
simple_file_sink_mt
>
(
"logger"
,
filename
,
true
);
logger
->
flush_on
(
spdlog
::
level
::
info
);
logger
->
set_error_handler
([
=
](
const
std
::
string
&
msg
)
{
throw
custom_ex
();
});
logger
->
info
(
"Good message #1"
);
REQUIRE_THROWS_AS
(
logger
->
info
(
"Bad format msg {} {}"
,
"xxx"
),
custom_ex
);
logger
->
info
(
"Good message #2"
);
REQUIRE
(
count_lines
(
filename
)
==
2
);
}
TEST_CASE
(
"default_error_handler2"
,
"[errors]]"
)
{
auto
logger
=
spdlog
::
create
<
failing_sink
>
(
"failed_logger"
);
logger
->
set_error_handler
([
=
](
const
std
::
string
&
msg
)
{
throw
custom_ex
();
});
REQUIRE_THROWS_AS
(
logger
->
info
(
"Some message"
),
custom_ex
);
auto
logger
=
spdlog
::
create
<
failing_sink
>
(
"failed_logger"
);
logger
->
set_error_handler
([
=
](
const
std
::
string
&
msg
)
{
throw
custom_ex
();
});
REQUIRE_THROWS_AS
(
logger
->
info
(
"Some message"
),
custom_ex
);
}
TEST_CASE
(
"async_error_handler"
,
"[errors]]"
)
{
prepare_logdir
();
std
::
string
err_msg
(
"log failed with some msg"
);
spdlog
::
set_async_mode
(
128
);
std
::
string
filename
=
"logs/simple_async_log.txt"
;
{
auto
logger
=
spdlog
::
create
<
spdlog
::
sinks
::
simple_file_sink_mt
>
(
"logger"
,
filename
,
true
);
logger
->
set_error_handler
([
=
](
const
std
::
string
&
msg
)
{
std
::
ofstream
ofs
(
"logs/custom_err.txt"
);
if
(
!
ofs
)
throw
std
::
runtime_error
(
"Failed open logs/custom_err.txt"
);
ofs
<<
err_msg
;
});
logger
->
info
(
"Good message #1"
);
logger
->
info
(
"Bad format msg {} {}"
,
"xxx"
);
logger
->
info
(
"Good message #2"
);
spdlog
::
drop
(
"logger"
);
//force logger to drain the queue and shutdown
spdlog
::
set_sync_mode
();
}
REQUIRE
(
count_lines
(
filename
)
==
2
);
REQUIRE
(
file_contents
(
"logs/custom_err.txt"
)
==
err_msg
);
prepare_logdir
();
std
::
string
err_msg
(
"log failed with some msg"
);
spdlog
::
set_async_mode
(
128
);
std
::
string
filename
=
"logs/simple_async_log.txt"
;
{
auto
logger
=
spdlog
::
create
<
spdlog
::
sinks
::
simple_file_sink_mt
>
(
"logger"
,
filename
,
true
);
logger
->
set_error_handler
([
=
](
const
std
::
string
&
msg
)
{
std
::
ofstream
ofs
(
"logs/custom_err.txt"
);
if
(
!
ofs
)
throw
std
::
runtime_error
(
"Failed open logs/custom_err.txt"
);
ofs
<<
err_msg
;
});
logger
->
info
(
"Good message #1"
);
logger
->
info
(
"Bad format msg {} {}"
,
"xxx"
);
logger
->
info
(
"Good message #2"
);
spdlog
::
drop
(
"logger"
);
//force logger to drain the queue and shutdown
spdlog
::
set_sync_mode
();
}
REQUIRE
(
count_lines
(
filename
)
==
2
);
REQUIRE
(
file_contents
(
"logs/custom_err.txt"
)
==
err_msg
);
}
// Make sure async error handler is executed
TEST_CASE
(
"async_error_handler2"
,
"[errors]]"
)
{
prepare_logdir
();
std
::
string
err_msg
(
"This is async handler error message"
);
spdlog
::
set_async_mode
(
128
);
{
auto
logger
=
spdlog
::
create
<
failing_sink
>
(
"failed_logger"
);
logger
->
set_error_handler
([
=
](
const
std
::
string
&
msg
)
{
std
::
ofstream
ofs
(
"logs/custom_err2.txt"
);
if
(
!
ofs
)
throw
std
::
runtime_error
(
"Failed open logs/custom_err2.txt"
);
ofs
<<
err_msg
;
});
logger
->
info
(
"Hello failure"
);
spdlog
::
drop
(
"failed_logger"
);
//force logger to drain the queue and shutdown
spdlog
::
set_sync_mode
();
}
REQUIRE
(
file_contents
(
"logs/custom_err2.txt"
)
==
err_msg
);
prepare_logdir
();
std
::
string
err_msg
(
"This is async handler error message"
);
spdlog
::
set_async_mode
(
128
);
{
auto
logger
=
spdlog
::
create
<
failing_sink
>
(
"failed_logger"
);
logger
->
set_error_handler
([
=
](
const
std
::
string
&
msg
)
{
std
::
ofstream
ofs
(
"logs/custom_err2.txt"
);
if
(
!
ofs
)
throw
std
::
runtime_error
(
"Failed open logs/custom_err2.txt"
);
ofs
<<
err_msg
;
});
logger
->
info
(
"Hello failure"
);
spdlog
::
drop
(
"failed_logger"
);
//force logger to drain the queue and shutdown
spdlog
::
set_sync_mode
();
}
REQUIRE
(
file_contents
(
"logs/custom_err2.txt"
)
==
err_msg
);
}
tests/utils.cpp
View file @
0c276bea
...
...
@@ -3,15 +3,15 @@
void
prepare_logdir
()
{
spdlog
::
drop_all
();
spdlog
::
drop_all
();
#ifdef _WIN32
system
(
"if not exist logs mkdir logs"
);
system
(
"if not exist logs mkdir logs"
);
system
(
"del /F /Q logs
\\
*"
);
#else
auto
rv
=
system
(
"mkdir -p logs"
);
auto
rv
=
system
(
"mkdir -p logs"
);
rv
=
system
(
"rm -f logs/*"
);
(
void
)
rv
;
#endif
#endif
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment