Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
S
spdlog
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Libraries
spdlog
Commits
3c4a2bf5
Commit
3c4a2bf5
authored
Dec 01, 2017
by
gabime
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Handle file extensions in rotating and daily loggers
parent
60ce47a8
Changes
8
Show whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
200 additions
and
37 deletions
+200
-37
README.md
README.md
+2
-2
example/example.cpp
example/example.cpp
+6
-7
include/spdlog/details/file_helper.h
include/spdlog/details/file_helper.h
+25
-5
include/spdlog/details/os.h
include/spdlog/details/os.h
+10
-0
include/spdlog/sinks/file_sinks.h
include/spdlog/sinks/file_sinks.h
+33
-22
tests/file_helper.cpp
tests/file_helper.cpp
+71
-0
tests/file_log.cpp
tests/file_log.cpp
+52
-0
tests/includes.h
tests/includes.h
+1
-1
No files found.
README.md
View file @
3c4a2bf5
...
@@ -113,12 +113,12 @@ int main(int, char*[])
...
@@ -113,12 +113,12 @@ int main(int, char*[])
my_logger
->
info
(
"Some log message"
);
my_logger
->
info
(
"Some log message"
);
// Create a file rotating logger with 5mb size max and 3 rotated files
// Create a file rotating logger with 5mb size max and 3 rotated files
auto
rotating_logger
=
spd
::
rotating_logger_mt
(
"some_logger_name"
,
"logs/mylogfile"
,
1048576
*
5
,
3
);
auto
rotating_logger
=
spd
::
rotating_logger_mt
(
"some_logger_name"
,
"logs/mylogfile
.txt
"
,
1048576
*
5
,
3
);
for
(
int
i
=
0
;
i
<
10
;
++
i
)
for
(
int
i
=
0
;
i
<
10
;
++
i
)
rotating_logger
->
info
(
"{} * {} equals {:>10}"
,
i
,
i
,
i
*
i
);
rotating_logger
->
info
(
"{} * {} equals {:>10}"
,
i
,
i
,
i
*
i
);
// Create a daily logger - a new file is created every day on 2:30am
// Create a daily logger - a new file is created every day on 2:30am
auto
daily_logger
=
spd
::
daily_logger_mt
(
"daily_logger"
,
"logs/daily"
,
2
,
30
);
auto
daily_logger
=
spd
::
daily_logger_mt
(
"daily_logger"
,
"logs/daily
.txt
"
,
2
,
30
);
// trigger flush if the log severity is error or higher
// trigger flush if the log severity is error or higher
daily_logger
->
flush_on
(
spd
::
level
::
err
);
daily_logger
->
flush_on
(
spd
::
level
::
err
);
daily_logger
->
info
(
123.44
);
daily_logger
->
info
(
123.44
);
...
...
example/example.cpp
View file @
3c4a2bf5
//
//
// Copyright(c) 2015 Gabi Melman.
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
//
...
@@ -43,16 +43,16 @@ int main(int, char*[])
...
@@ -43,16 +43,16 @@ int main(int, char*[])
// Create basic file logger (not rotated)
// Create basic file logger (not rotated)
auto
my_logger
=
spd
::
basic_logger_mt
(
"basic_logger"
,
"logs/basic"
);
auto
my_logger
=
spd
::
basic_logger_mt
(
"basic_logger"
,
"logs/basic
-log.txt
"
);
my_logger
->
info
(
"Some log message"
);
my_logger
->
info
(
"Some log message"
);
// Create a file rotating logger with 5mb size max and 3 rotated files
// Create a file rotating logger with 5mb size max and 3 rotated files
auto
rotating_logger
=
spd
::
rotating_logger_mt
(
"some_logger_name"
,
"logs/
mylogfile
"
,
1048576
*
5
,
3
);
auto
rotating_logger
=
spd
::
rotating_logger_mt
(
"some_logger_name"
,
"logs/
rotating.txt
"
,
1048576
*
5
,
3
);
for
(
int
i
=
0
;
i
<
10
;
++
i
)
for
(
int
i
=
0
;
i
<
10
;
++
i
)
rotating_logger
->
info
(
"{} * {} equals {:>10}"
,
i
,
i
,
i
*
i
);
rotating_logger
->
info
(
"{} * {} equals {:>10}"
,
i
,
i
,
i
*
i
);
// Create a daily logger - a new file is created every day on 2:30am
// Create a daily logger - a new file is created every day on 2:30am
auto
daily_logger
=
spd
::
daily_logger_mt
(
"daily_logger"
,
"logs/daily"
,
2
,
30
);
auto
daily_logger
=
spd
::
daily_logger_mt
(
"daily_logger"
,
"logs/daily
.txt
"
,
2
,
30
);
// trigger flush if the log severity is error or higher
// trigger flush if the log severity is error or higher
daily_logger
->
flush_on
(
spd
::
level
::
err
);
daily_logger
->
flush_on
(
spd
::
level
::
err
);
daily_logger
->
info
(
123.44
);
daily_logger
->
info
(
123.44
);
...
@@ -111,8 +111,7 @@ void async_example()
...
@@ -111,8 +111,7 @@ void async_example()
{
{
size_t
q_size
=
4096
;
//queue size must be power of 2
size_t
q_size
=
4096
;
//queue size must be power of 2
spdlog
::
set_async_mode
(
q_size
);
spdlog
::
set_async_mode
(
q_size
);
auto
async_file
=
spd
::
daily_logger_st
(
"async_file_logger"
,
"logs/async_log"
);
auto
async_file
=
spd
::
daily_logger_st
(
"async_file_logger"
,
"logs/async_log.txt"
);
for
(
int
i
=
0
;
i
<
100
;
++
i
)
for
(
int
i
=
0
;
i
<
100
;
++
i
)
async_file
->
info
(
"Async message #{}"
,
i
);
async_file
->
info
(
"Async message #{}"
,
i
);
}
}
...
...
include/spdlog/details/file_helper.h
View file @
3c4a2bf5
...
@@ -16,6 +16,7 @@
...
@@ -16,6 +16,7 @@
#include <cstdio>
#include <cstdio>
#include <string>
#include <string>
#include <thread>
#include <thread>
#include <tuple>
#include <cerrno>
#include <cerrno>
namespace
spdlog
namespace
spdlog
...
@@ -84,14 +85,13 @@ public:
...
@@ -84,14 +85,13 @@ public:
void
write
(
const
log_msg
&
msg
)
void
write
(
const
log_msg
&
msg
)
{
{
size_t
msg_size
=
msg
.
formatted
.
size
();
size_t
msg_size
=
msg
.
formatted
.
size
();
auto
data
=
msg
.
formatted
.
data
();
auto
data
=
msg
.
formatted
.
data
();
if
(
std
::
fwrite
(
data
,
1
,
msg_size
,
_fd
)
!=
msg_size
)
if
(
std
::
fwrite
(
data
,
1
,
msg_size
,
_fd
)
!=
msg_size
)
throw
spdlog_ex
(
"Failed writing to file "
+
os
::
filename_to_str
(
_filename
),
errno
);
throw
spdlog_ex
(
"Failed writing to file "
+
os
::
filename_to_str
(
_filename
),
errno
);
}
}
size_t
size
()
size_t
size
()
const
{
{
if
(
!
_fd
)
if
(
!
_fd
)
throw
spdlog_ex
(
"Cannot use size() on closed file "
+
os
::
filename_to_str
(
_filename
));
throw
spdlog_ex
(
"Cannot use size() on closed file "
+
os
::
filename_to_str
(
_filename
));
...
@@ -103,10 +103,30 @@ public:
...
@@ -103,10 +103,30 @@ public:
return
_filename
;
return
_filename
;
}
}
static
bool
file_exists
(
const
filename_t
&
name
)
static
bool
file_exists
(
const
filename_t
&
f
name
)
{
{
return
os
::
file_exists
(
fname
);
}
return
os
::
file_exists
(
name
);
//
// return basename and extension:
//
// "mylog.txt" => ("mylog", ".txt")
// "mylog" => ("mylog", "")
//
// the starting dot in filenames is ignored (hidden files):
//
// "my_folder/.mylog" => ("my_folder/.mylog")
// "my_folder/.mylog.txt" => ("my_folder/.mylog", ".txt")
static
std
::
tuple
<
filename_t
,
filename_t
>
split_by_extenstion
(
const
filename_t
&
fname
)
{
auto
index
=
fname
.
rfind
(
'.'
);
bool
found_ext
=
index
!=
filename_t
::
npos
&&
index
!=
0
&&
fname
[
index
-
1
]
!=
details
::
os
::
folder_sep
;
if
(
found_ext
)
return
std
::
make_tuple
(
fname
.
substr
(
0
,
index
),
fname
.
substr
(
index
));
else
return
std
::
make_tuple
(
fname
,
filename_t
());
}
}
private:
private:
...
...
include/spdlog/details/os.h
View file @
3c4a2bf5
...
@@ -143,6 +143,16 @@ inline bool operator!=(const std::tm& tm1, const std::tm& tm2)
...
@@ -143,6 +143,16 @@ inline bool operator!=(const std::tm& tm1, const std::tm& tm2)
SPDLOG_CONSTEXPR
static
const
char
*
eol
=
SPDLOG_EOL
;
SPDLOG_CONSTEXPR
static
const
char
*
eol
=
SPDLOG_EOL
;
SPDLOG_CONSTEXPR
static
int
eol_size
=
sizeof
(
SPDLOG_EOL
)
-
1
;
SPDLOG_CONSTEXPR
static
int
eol_size
=
sizeof
(
SPDLOG_EOL
)
-
1
;
// folder separator
#ifdef _WIN32
SPDLOG_CONSTEXPR
static
const
char
folder_sep
=
'\\'
;
#else
SPDLOG_CONSTEXPR
static
const
char
folder_sep
=
'/'
;
#endif
inline
void
prevent_child_fd
(
FILE
*
f
)
inline
void
prevent_child_fd
(
FILE
*
f
)
{
{
#ifdef _WIN32
#ifdef _WIN32
...
...
include/spdlog/sinks/file_sinks.h
View file @
3c4a2bf5
...
@@ -77,6 +77,23 @@ public:
...
@@ -77,6 +77,23 @@ public:
_current_size
=
_file_helper
.
size
();
//expensive. called only once
_current_size
=
_file_helper
.
size
();
//expensive. called only once
}
}
// calc filename according to index and file extension if exists.
// e.g. calc_filename("logs/mylog.txt, 3) => "logs/mylog.3.txt".
static
filename_t
calc_filename
(
const
filename_t
&
filename
,
std
::
size_t
index
)
{
std
::
conditional
<
std
::
is_same
<
filename_t
::
value_type
,
char
>::
value
,
fmt
::
MemoryWriter
,
fmt
::
WMemoryWriter
>::
type
w
;
if
(
index
)
{
filename_t
basename
,
ext
;
std
::
tie
(
basename
,
ext
)
=
details
::
file_helper
::
split_by_extenstion
(
filename
);
w
.
write
(
SPDLOG_FILENAME_T
(
"{}.{}{}"
),
basename
,
index
,
ext
);
}
else
{
w
.
write
(
SPDLOG_FILENAME_T
(
"{}"
),
filename
);
}
return
w
.
str
();
}
protected:
protected:
void
_sink_it
(
const
details
::
log_msg
&
msg
)
override
void
_sink_it
(
const
details
::
log_msg
&
msg
)
override
...
@@ -95,23 +112,13 @@ protected:
...
@@ -95,23 +112,13 @@ protected:
_file_helper
.
flush
();
_file_helper
.
flush
();
}
}
private:
static
filename_t
calc_filename
(
const
filename_t
&
filename
,
std
::
size_t
index
)
{
std
::
conditional
<
std
::
is_same
<
filename_t
::
value_type
,
char
>::
value
,
fmt
::
MemoryWriter
,
fmt
::
WMemoryWriter
>::
type
w
;
if
(
index
)
w
.
write
(
SPDLOG_FILENAME_T
(
"{}.{}"
),
filename
,
index
);
else
w
.
write
(
SPDLOG_FILENAME_T
(
"{}"
),
filename
);
return
w
.
str
();
}
private:
// Rotate files:
// Rotate files:
// log.txt -> log.txt.1
// log.txt -> log.1.txt
// log.txt.1 -> log.txt.2
// log.1.txt -> log.2.txt
// log.txt.2 -> log.txt.3
// log.2.txt -> log.3.txt
// lo3.txt.3 -> delete
// log.3.txt -> delete
void
_rotate
()
void
_rotate
()
{
{
using
details
::
os
::
filename_to_str
;
using
details
::
os
::
filename_to_str
;
...
@@ -150,27 +157,31 @@ typedef rotating_file_sink<details::null_mutex>rotating_file_sink_st;
...
@@ -150,27 +157,31 @@ typedef rotating_file_sink<details::null_mutex>rotating_file_sink_st;
*/
*/
struct
default_daily_file_name_calculator
struct
default_daily_file_name_calculator
{
{
// Create filename for the form
basename.YYYY-MM-DD_hh-mm
// Create filename for the form
filename.YYYY-MM-DD_hh-mm.ext
static
filename_t
calc_filename
(
const
filename_t
&
bas
ename
)
static
filename_t
calc_filename
(
const
filename_t
&
fil
ename
)
{
{
std
::
tm
tm
=
spdlog
::
details
::
os
::
localtime
();
std
::
tm
tm
=
spdlog
::
details
::
os
::
localtime
();
filename_t
basename
,
ext
;
std
::
tie
(
basename
,
ext
)
=
details
::
file_helper
::
split_by_extenstion
(
filename
);
std
::
conditional
<
std
::
is_same
<
filename_t
::
value_type
,
char
>::
value
,
fmt
::
MemoryWriter
,
fmt
::
WMemoryWriter
>::
type
w
;
std
::
conditional
<
std
::
is_same
<
filename_t
::
value_type
,
char
>::
value
,
fmt
::
MemoryWriter
,
fmt
::
WMemoryWriter
>::
type
w
;
w
.
write
(
SPDLOG_FILENAME_T
(
"{}_{:04d}-{:02d}-{:02d}_{:02d}-{:02d}
"
),
basename
,
tm
.
tm_year
+
1900
,
tm
.
tm_mon
+
1
,
tm
.
tm_mday
,
tm
.
tm_hour
,
tm
.
tm_min
);
w
.
write
(
SPDLOG_FILENAME_T
(
"{}_{:04d}-{:02d}-{:02d}_{:02d}-{:02d}
{}"
),
basename
,
tm
.
tm_year
+
1900
,
tm
.
tm_mon
+
1
,
tm
.
tm_mday
,
tm
.
tm_hour
,
tm
.
tm_min
,
ext
);
return
w
.
str
();
return
w
.
str
();
}
}
};
};
/*
/*
* Generator of daily log file names in format basename.YYYY-MM-DD
* Generator of daily log file names in format basename.YYYY-MM-DD
.ext
*/
*/
struct
dateonly_daily_file_name_calculator
struct
dateonly_daily_file_name_calculator
{
{
// Create filename for the form basename.YYYY-MM-DD
// Create filename for the form basename.YYYY-MM-DD
static
filename_t
calc_filename
(
const
filename_t
&
bas
ename
)
static
filename_t
calc_filename
(
const
filename_t
&
fil
ename
)
{
{
std
::
tm
tm
=
spdlog
::
details
::
os
::
localtime
();
std
::
tm
tm
=
spdlog
::
details
::
os
::
localtime
();
filename_t
basename
,
ext
;
std
::
tie
(
basename
,
ext
)
=
details
::
file_helper
::
split_by_extenstion
(
filename
);
std
::
conditional
<
std
::
is_same
<
filename_t
::
value_type
,
char
>::
value
,
fmt
::
MemoryWriter
,
fmt
::
WMemoryWriter
>::
type
w
;
std
::
conditional
<
std
::
is_same
<
filename_t
::
value_type
,
char
>::
value
,
fmt
::
MemoryWriter
,
fmt
::
WMemoryWriter
>::
type
w
;
w
.
write
(
SPDLOG_FILENAME_T
(
"{}_{:04d}-{:02d}-{:02d}
"
),
basename
,
tm
.
tm_year
+
1900
,
tm
.
tm_mon
+
1
,
tm
.
tm_mday
);
w
.
write
(
SPDLOG_FILENAME_T
(
"{}_{:04d}-{:02d}-{:02d}
{}"
),
basename
,
tm
.
tm_year
+
1900
,
tm
.
tm_mon
+
1
,
tm
.
tm_mday
,
ext
);
return
w
.
str
();
return
w
.
str
();
}
}
};
};
...
...
tests/file_helper.cpp
View file @
3c4a2bf5
...
@@ -73,6 +73,77 @@ TEST_CASE("file_helper_reopen2", "[file_helper::reopen(false)]]")
...
@@ -73,6 +73,77 @@ TEST_CASE("file_helper_reopen2", "[file_helper::reopen(false)]]")
REQUIRE
(
helper
.
size
()
==
expected_size
);
REQUIRE
(
helper
.
size
()
==
expected_size
);
}
}
TEST_CASE
(
"file_helper_split_by_extenstion"
,
"[file_helper::split_by_extenstion()]]"
)
{
std
::
string
basename
,
ext
;
std
::
tie
(
basename
,
ext
)
=
file_helper
::
split_by_extenstion
(
"mylog.txt"
);
REQUIRE
(
basename
==
"mylog"
);
REQUIRE
(
ext
==
".txt"
);
}
TEST_CASE
(
"file_helper_split_by_extenstion2"
,
"[file_helper::split_by_extenstion()]]"
)
{
std
::
string
basename
,
ext
;
std
::
tie
(
basename
,
ext
)
=
file_helper
::
split_by_extenstion
(
"mylog"
);
REQUIRE
(
basename
==
"mylog"
);
REQUIRE
(
ext
==
""
);
}
TEST_CASE
(
"file_helper_split_by_extenstion3"
,
"[file_helper::split_by_extenstion()]]"
)
{
std
::
string
basename
,
ext
;
std
::
tie
(
basename
,
ext
)
=
file_helper
::
split_by_extenstion
(
"mylog.xyz.txt"
);
REQUIRE
(
basename
==
"mylog.xyz"
);
REQUIRE
(
ext
==
".txt"
);
}
TEST_CASE
(
"file_helper_split_by_extenstion4"
,
"[file_helper::split_by_extenstion()]]"
)
{
std
::
string
basename
,
ext
;
std
::
tie
(
basename
,
ext
)
=
file_helper
::
split_by_extenstion
(
"mylog.xyz....txt"
);
REQUIRE
(
basename
==
"mylog.xyz..."
);
REQUIRE
(
ext
==
".txt"
);
}
TEST_CASE
(
"file_helper_split_by_extenstion5"
,
"[file_helper::split_by_extenstion(hidden_file)]]"
)
{
std
::
string
basename
,
ext
;
std
::
tie
(
basename
,
ext
)
=
file_helper
::
split_by_extenstion
(
".mylog"
);
REQUIRE
(
basename
==
".mylog"
);
REQUIRE
(
ext
==
""
);
}
TEST_CASE
(
"file_helper_split_by_extenstion6"
,
"[file_helper::split_by_extenstion(hidden_file)]]"
)
{
#ifdef _WIN32
auto
filename
=
"folder
\\
.mylog"
;
auto
expected_basename
=
"folder
\\
.mylog"
;
#else
auto
filename
=
"folder/.mylog"
;
auto
expected_basename
=
"folder/.mylog"
;
#endif
std
::
string
basename
,
ext
;
std
::
tie
(
basename
,
ext
)
=
file_helper
::
split_by_extenstion
(
filename
);
REQUIRE
(
basename
==
expected_basename
);
REQUIRE
(
ext
==
""
);
}
TEST_CASE
(
"file_helper_split_by_extenstion7"
,
"[file_helper::split_by_extenstion(hidden_file)]]"
)
{
#ifdef _WIN32
auto
filename
=
"folder
\\
.mylog.txt"
;
auto
expected_basename
=
"folder
\\
.mylog"
;
#else
auto
filename
=
"folder/.mylog.txt"
;
auto
expected_basename
=
"folder//.mylog"
;
#endif
std
::
string
basename
,
ext
;
std
::
tie
(
basename
,
ext
)
=
file_helper
::
split_by_extenstion
(
filename
);
REQUIRE
(
basename
==
expected_basename
);
REQUIRE
(
ext
==
".txt"
);
}
tests/file_log.cpp
View file @
3c4a2bf5
...
@@ -188,3 +188,55 @@ TEST_CASE("daily_logger with custom calculator", "[daily_logger_custom]]")
...
@@ -188,3 +188,55 @@ TEST_CASE("daily_logger with custom calculator", "[daily_logger_custom]]")
REQUIRE
(
count_lines
(
filename
)
==
10
);
REQUIRE
(
count_lines
(
filename
)
==
10
);
}
}
/*
* File name calculations
*/
TEST_CASE
(
"rotating_file_sink::calc_filename1"
,
"[rotating_file_sink]]"
)
{
auto
filename
=
spdlog
::
sinks
::
rotating_file_sink_st
::
calc_filename
(
"rotated.txt"
,
3
);
REQUIRE
(
filename
==
"rotated.3.txt"
);
}
TEST_CASE
(
"rotating_file_sink::calc_filename2"
,
"[rotating_file_sink]]"
)
{
auto
filename
=
spdlog
::
sinks
::
rotating_file_sink_st
::
calc_filename
(
"rotated"
,
3
);
REQUIRE
(
filename
==
"rotated.3"
);
}
TEST_CASE
(
"rotating_file_sink::calc_filename3"
,
"[rotating_file_sink]]"
)
{
auto
filename
=
spdlog
::
sinks
::
rotating_file_sink_st
::
calc_filename
(
"rotated.txt"
,
0
);
REQUIRE
(
filename
==
"rotated.txt"
);
}
TEST_CASE
(
"daily_file_sink::default_daily_file_name_calculator1"
,
"[daily_file_sink]]"
)
{
// daily.YYYY-MM-DD_hh-mm.txt
auto
filename
=
spdlog
::
sinks
::
default_daily_file_name_calculator
::
calc_filename
(
"daily.txt"
);
std
::
regex
re
(
R"(^daily_(19|20)\d\d-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])_\d\d-[0-5][0-9].txt$)"
);
std
::
smatch
match
;
REQUIRE
(
std
::
regex_match
(
filename
,
match
,
re
));
}
TEST_CASE
(
"daily_file_sink::default_daily_file_name_calculator2"
,
"[daily_file_sink]]"
)
{
// daily.YYYY-MM-DD_hh-mm.txt
auto
filename
=
spdlog
::
sinks
::
default_daily_file_name_calculator
::
calc_filename
(
"daily"
);
std
::
regex
re
(
R"(^daily_(19|20)\d\d-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])_\d\d-[0-5][0-9]$)"
);
std
::
smatch
match
;
REQUIRE
(
std
::
regex_match
(
filename
,
match
,
re
));
}
TEST_CASE
(
"daily_file_sink::dateonly_daily_file_name_calculator"
,
"[daily_file_sink]]"
)
{
// daily.YYYY-MM-DD_hh-mm.txt
auto
filename
=
spdlog
::
sinks
::
dateonly_daily_file_name_calculator
::
calc_filename
(
"daily.txt"
);
// date regex based on https://www.regular-expressions.info/dates.html
std
::
regex
re
(
R"(^daily_(19|20)\d\d-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])\.txt$)"
);
std
::
smatch
match
;
REQUIRE
(
std
::
regex_match
(
filename
,
match
,
re
));
}
tests/includes.h
View file @
3c4a2bf5
...
@@ -6,7 +6,7 @@
...
@@ -6,7 +6,7 @@
#include <ostream>
#include <ostream>
#include <chrono>
#include <chrono>
#include <exception>
#include <exception>
#include <regex>
#include "catch.hpp"
#include "catch.hpp"
#include "utils.h"
#include "utils.h"
...
...
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