├── .github
└── workflows
│ └── ci.yaml
├── .gitignore
├── .gitmodules
├── .project
├── .pydevproject
├── LICENSE
├── MANIFEST.in
├── README.md
├── create_dist.sh
├── setup.cfg
├── setup.py
├── src
└── pyspdlog.cpp
├── tests
├── sink_test.py
├── spdlog_vs_logging.py
└── test_spdlog.py
└── upload_to_pypi.sh
/.github/workflows/ci.yaml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | - push
5 | - pull_request
6 |
7 | jobs:
8 | build:
9 | name: "🐍 Ubuntu 20.04 - Python ${{ matrix.python-version }}"
10 | runs-on: ubuntu-20.04
11 | strategy:
12 | fail-fast: false
13 | matrix:
14 | python-version: ['3.6', '3.7', '3.8', '3.9', '3.10']
15 |
16 | steps:
17 | - name: Checkout
18 | uses: actions/checkout@v3
19 | with:
20 | submodules: true
21 |
22 | - name: Setup Python ${{ matrix.python-version }}
23 | uses: actions/setup-python@v3
24 | with:
25 | python-version: ${{ matrix.python-version }}
26 |
27 | - name: Dependencies
28 | run: |
29 | python -m pip install pytest
30 |
31 | - name: Install
32 | run: |
33 | python -m pip install -v .
34 |
35 | - name: Test
36 | run: |
37 | python -m pytest -vs tests
38 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | **/*.swp
2 | *.so
3 | Makefile
4 | *.log
5 | build/
6 | dist/
7 | .eggs/
8 | *.egg-info/
9 | .project
10 | .pydevproject
11 | tests/__pycache__/
12 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "spdlog"]
2 | path = spdlog
3 | url = https://github.com/gabime/spdlog.git
4 |
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | spdlog-python.git
4 |
5 |
6 |
7 |
8 |
9 | org.python.pydev.PyDevBuilder
10 |
11 |
12 |
13 |
14 |
15 | org.python.pydev.pythonNature
16 |
17 |
18 |
--------------------------------------------------------------------------------
/.pydevproject:
--------------------------------------------------------------------------------
1 |
2 |
3 | Default
4 | python interpreter
5 |
6 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 bodgergely
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include README.md LICENSE setup.py setup.cfg
2 | graft src
3 | graft spdlog/include/spdlog
4 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://github.com/bodgergely/spdlog-python/actions)
2 |
3 | spdlog-python
4 | =============
5 |
6 | python wrapper around the fast C++ logger called [spdlog](https://github.com/gabime/spdlog)
7 |
8 |
9 | Introduction
10 | ============
11 |
12 | Python wrapper (pybind11) around the C++ spdlog logging library.
13 |
14 | Why choose [spdlog](https://github.com/gabime/spdlog)?
15 |
16 | https://kjellkod.wordpress.com/2015/06/30/the-worlds-fastest-logger-vs-g3log/
17 |
18 | Try running [tests/spdlog_vs_logging.py](https://github.com/bodgergely/spdlog-python/blob/master/tests/test_spdlog.py) and see what results you get on your system.
19 |
20 | spdlog-python vs logging (standard lib)
21 | ---------------------------------------
22 |
23 | How many microseconds it takes on average to complete a log function (info(), debug() etc) using a FileLogger.
24 | On reasonable sized log messages spdlog takes **4% (async mode enabled)** and **6% (sync mode)** of the time it would take to complete using the standard logging module.
25 |
26 | Async mode with 8MB queue with blocking mode.
27 |
28 | | msg len (bytes) | spdlog **sync** (microsec)| spdlog **async** (microsec)| logging (microsec) |
29 | | ------- | :--------: | :--------: | :--------: |
30 | | 10 | 1.2 | 0.87 | 24.6 |
31 | | 100 | 1.2 | 1.03 | 24.6 |
32 | | 300 | 1.5 | 1.07 | 24.9 |
33 | | 1000 | 2.4 | 1.16 | 26.8 |
34 | | 5000 | 6.2 | 2.31 | 31.7 |
35 | | 20000 | 15.3 | 7.51 | 48.0 |
36 |
37 | Installation
38 | ============
39 |
40 | 1) `pip install spdlog` will get a distribution from pypi.org
41 |
42 | or
43 |
44 | 2) from github:
45 |
46 | `pip install pybind11` - if missing
47 |
48 | ```bash
49 | git clone https://github.com/bodgergely/spdlog-python.git
50 | cd spdlog-python
51 | git submodule update --init --recursive
52 | python setup.py install
53 | ```
54 |
55 | Usage
56 | =====
57 |
58 | ```python
59 | ./python
60 | import spdlog as spd
61 | logger = spd.FileLogger('fast_logger', '/tmp/spdlog_example.log')
62 | logger.set_level(spd.LogLevel.INFO)
63 | logger.info('Hello World!')
64 | logger.debug('I am not so important.')
65 | ```
66 |
67 | To run the speed test:
68 |
69 | ```bash
70 | python ./tests/spdlog_vs_logging.py
71 | ```
72 |
--------------------------------------------------------------------------------
/create_dist.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | python setup.py sdist
3 | #python3 setup.py sdist bdist_wheel
4 |
--------------------------------------------------------------------------------
/setup.cfg:
--------------------------------------------------------------------------------
1 | [aliases]
2 | test=pytest
3 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | import os
2 | import platform
3 | import sys
4 |
5 | import sysconfig
6 | from setuptools import setup
7 | from setuptools.extension import Extension
8 | from distutils.command.install_headers import install_headers
9 |
10 | def is_posix():
11 | return platform.os.name == "posix"
12 |
13 | def link_libs():
14 | libs = []
15 | if is_posix():
16 | libs.append("stdc++")
17 | return libs
18 |
19 | class get_pybind_include(object):
20 | def __init__(self, user=False):
21 | self.user = user
22 |
23 | def __str__(self):
24 | import pybind11
25 | return pybind11.get_include(self.user)
26 |
27 | def get_include_dirs():
28 | include_dirs=[
29 | 'spdlog/include/',
30 | get_pybind_include(),
31 | get_pybind_include(user=True),
32 | ]
33 |
34 | conda_prefix = os.environ.get('CONDA_PREFIX')
35 | if conda_prefix is not None:
36 | include_dirs.append(os.path.join(conda_prefix, "include"))
37 |
38 | return include_dirs
39 |
40 |
41 | def include_dir_files(folder):
42 | """Find all C++ header files in folder"""
43 | from os import walk
44 | files = []
45 | for (dirpath, _, filenames) in walk(folder):
46 | for fn in filenames:
47 | if os.path.splitext(fn)[1] in {'.h', '.hpp'}:
48 | files.append(os.path.join(dirpath, fn))
49 | return files
50 |
51 | class install_headers_subdir(install_headers):
52 | """Install headers and keep subfolder structure"""
53 | def run(self):
54 | headers = self.distribution.headers or []
55 | for header in headers:
56 | submod_dir = os.path.dirname(os.path.relpath(header, 'spdlog/include/spdlog'))
57 | install_dir = os.path.join(self.install_dir, submod_dir)
58 | self.mkpath(install_dir)
59 | (out, _) = self.copy_file(header, install_dir)
60 | self.outfiles.append(out)
61 |
62 | setup(
63 | name='spdlog',
64 | version='2.0.6',
65 | author='Gergely Bod',
66 | author_email='bodgergely@hotmail.com',
67 | description='python wrapper around C++ spdlog logging library (https://github.com/bodgergely/spdlog-python)',
68 | license='MIT',
69 | long_description='python wrapper (https://github.com/bodgergely/spdlog-python) around C++ spdlog (http://github.com/gabime/spdlog.git) logging library.',
70 | setup_requires=['pybind11>=2.2', 'wheel', 'pytest-runner'],
71 | install_requires=['pybind11>=2.2'],
72 | tests_require=['pytest'],
73 | ext_modules=[
74 | Extension(
75 | 'spdlog',
76 | ['src/pyspdlog.cpp'],
77 | include_dirs=get_include_dirs(),
78 | libraries=link_libs(),
79 | extra_compile_args=["-std=c++11", "-v"],
80 | language='c++11'
81 | )
82 | ],
83 | headers=include_dir_files('spdlog/include/spdlog'),
84 | cmdclass={'install_headers': install_headers_subdir},
85 | zip_safe=False,
86 | )
87 |
--------------------------------------------------------------------------------
/src/pyspdlog.cpp:
--------------------------------------------------------------------------------
1 | #ifndef _WIN32
2 | #define SPDLOG_ENABLE_SYSLOG
3 | #endif
4 |
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 |
11 | using namespace pybind11::literals;
12 |
13 | #include
14 | #include
15 | #include
16 | #include
17 | #include
18 | #include
19 | #include
20 | #include
21 | #include
22 | #include
23 | #include
24 | #include
25 | #include
26 | #ifndef _WIN32
27 | #include
28 | #endif
29 |
30 | #include
31 | #include
32 | #include
33 | #include
34 | #include
35 | #include
36 | #include
37 |
38 | namespace spd = spdlog;
39 | namespace py = pybind11;
40 |
41 | namespace { // Avoid cluttering the global namespace.
42 |
43 | class Logger;
44 |
45 | bool g_async_mode_on = false;
46 | auto g_async_overflow_policy = spdlog::async_overflow_policy::block;
47 |
48 | std::unordered_map g_loggers;
49 | std::mutex mutex_loggers;
50 |
51 | void register_logger(const std::string& name, Logger* logger)
52 | {
53 | std::lock_guard lck(mutex_loggers);
54 | g_loggers[name] = logger;
55 | }
56 |
57 | Logger* access_logger(const std::string& name)
58 | {
59 | std::lock_guard lck(mutex_loggers);
60 | return g_loggers[name];
61 | }
62 |
63 | void remove_logger(const std::string& name)
64 | {
65 | std::lock_guard lck(mutex_loggers);
66 | g_loggers[name] = nullptr;
67 | g_loggers.erase(name);
68 | }
69 |
70 | void remove_logger_all()
71 | {
72 | std::lock_guard lck(mutex_loggers);
73 | g_loggers.clear();
74 | }
75 |
76 | class LogLevel {
77 | public:
78 | const static int trace{ (int)spd::level::trace };
79 | const static int debug{ (int)spd::level::debug };
80 | const static int info{ (int)spd::level::info };
81 | const static int warn{ (int)spd::level::warn };
82 | const static int err{ (int)spd::level::err };
83 | const static int critical{ (int)spd::level::critical };
84 | const static int off{ (int)spd::level::off };
85 | };
86 |
87 | class Sink {
88 | public:
89 | Sink() {}
90 | Sink(const spd::sink_ptr& sink)
91 | : _sink(sink)
92 | {
93 | }
94 | virtual ~Sink() {}
95 | virtual void log(const spd::details::log_msg& msg)
96 | {
97 | _sink->log(msg);
98 | }
99 | bool should_log(int msg_level) const
100 | {
101 | return _sink->should_log((spd::level::level_enum)msg_level);
102 | }
103 | void set_level(int log_level)
104 | {
105 | _sink->set_level((spd::level::level_enum)log_level);
106 | }
107 | int level() const
108 | {
109 | return (int)_sink->level();
110 | }
111 |
112 | spd::sink_ptr get_sink() const { return _sink; }
113 |
114 | protected:
115 | spd::sink_ptr _sink{ nullptr };
116 | };
117 |
118 | // template
119 | // class generic_sink : public Sink {
120 | // public:
121 | // generic_sink() {
122 | // _sink = std::make_shared();
123 | // }
124 | // };
125 |
126 | // class stdout_sink_st : public generic_sink { };
127 | // class stdout_sink_mt : public generic_sink { };
128 |
129 | class stdout_sink_st : public Sink {
130 | public:
131 | stdout_sink_st()
132 | {
133 | _sink = std::make_shared();
134 | }
135 | };
136 |
137 | class stdout_sink_mt : public Sink {
138 | public:
139 | stdout_sink_mt()
140 | {
141 | _sink = std::make_shared();
142 | }
143 | };
144 |
145 | class stdout_color_sink_st : public Sink {
146 | public:
147 | stdout_color_sink_st()
148 | {
149 | _sink = std::make_shared();
150 | }
151 | };
152 |
153 | class stdout_color_sink_mt : public Sink {
154 | public:
155 | stdout_color_sink_mt()
156 | {
157 | _sink = std::make_shared();
158 | }
159 | };
160 |
161 | class stderr_sink_st : public Sink {
162 | public:
163 | stderr_sink_st()
164 | {
165 | _sink = std::make_shared();
166 | }
167 | };
168 |
169 | class stderr_sink_mt : public Sink {
170 | public:
171 | stderr_sink_mt()
172 | {
173 | _sink = std::make_shared();
174 | }
175 | };
176 |
177 | class stderr_color_sink_st : public Sink {
178 | public:
179 | stderr_color_sink_st()
180 | {
181 | _sink = std::make_shared();
182 | }
183 | };
184 |
185 | class stderr_color_sink_mt : public Sink {
186 | public:
187 | stderr_color_sink_mt()
188 | {
189 | _sink = std::make_shared();
190 | }
191 | };
192 |
193 | class basic_file_sink_st : public Sink {
194 | public:
195 | basic_file_sink_st(const std::string& base_filename, bool truncate)
196 | {
197 | _sink = std::make_shared(base_filename, truncate);
198 | }
199 | };
200 |
201 | class basic_file_sink_mt : public Sink {
202 | public:
203 | basic_file_sink_mt(const std::string& base_filename, bool truncate)
204 | {
205 | _sink = std::make_shared(base_filename, truncate);
206 | }
207 | };
208 |
209 | class daily_file_sink_mt : public Sink {
210 | public:
211 | daily_file_sink_mt(const std::string& base_filename, int rotation_hour, int rotation_minute)
212 | {
213 | _sink = std::make_shared(base_filename, rotation_hour, rotation_minute);
214 | }
215 | };
216 |
217 | class daily_file_sink_st : public Sink {
218 | public:
219 | daily_file_sink_st(const std::string& base_filename, int rotation_hour, int rotation_minute)
220 | {
221 | _sink = std::make_shared(base_filename, rotation_hour, rotation_minute);
222 | }
223 | };
224 |
225 | class rotating_file_sink_mt : public Sink {
226 | public:
227 | rotating_file_sink_mt(const std::string& filename, size_t max_file_size, size_t max_files)
228 | {
229 | _sink = std::make_shared(filename, max_file_size, max_files);
230 | }
231 | };
232 |
233 | class rotating_file_sink_st : public Sink {
234 | public:
235 | rotating_file_sink_st(const std::string& filename, size_t max_file_size, size_t max_files)
236 | {
237 | _sink = std::make_shared(filename, max_file_size, max_files);
238 | }
239 | };
240 |
241 | template
242 | class dist_sink: public Sink {
243 | public:
244 | dist_sink() { _sink = std::make_shared>();}
245 | dist_sink(std::vector sinks)
246 | {
247 | std::vector sink_vec;
248 | for (uint i =0; i>(sink_vec);
253 | }
254 | void add_sink(const Sink& sink)
255 | {
256 | std::dynamic_pointer_cast>(_sink)->add_sink(sink.get_sink());
257 | }
258 |
259 | void remove_sink(const Sink& sink)
260 | {
261 | std::dynamic_pointer_cast>(_sink)->remove_sink(sink.get_sink());
262 | }
263 |
264 | void set_sinks(std::vector sinks)
265 | {
266 | std::vector sink_vec;
267 | for (uint i =0; i>(_sink)->set_sinks(sink_vec);
272 | }
273 |
274 | std::vector &sinks()
275 | {
276 | return std::dynamic_pointer_cast>(_sink)->sinks();
277 | }
278 | };
279 |
280 | using dist_sink_mt = dist_sink;
281 | using dist_sink_st = dist_sink;
282 |
283 | class dup_filter_sink_mt: public dist_sink_mt {
284 | public:
285 | dup_filter_sink_mt(float max_skip_duration_sec)
286 | {
287 | _sink = std::make_shared(std::chrono::milliseconds((int)(max_skip_duration_sec*1000.0)));
288 | }
289 | };
290 |
291 | class dup_filter_sink_st: public dist_sink_st {
292 | public:
293 | dup_filter_sink_st(float max_skip_duration_sec)
294 | {
295 | _sink = std::make_shared(std::chrono::milliseconds((int)(max_skip_duration_sec*1000.0)));
296 | }
297 | };
298 |
299 | class null_sink_st : public Sink {
300 | public:
301 | null_sink_st()
302 | {
303 | _sink = std::make_shared();
304 | }
305 | };
306 |
307 | class null_sink_mt : public Sink {
308 | public:
309 | null_sink_mt()
310 | {
311 | _sink = std::make_shared();
312 | }
313 | };
314 |
315 | class tcp_sink_st : public Sink {
316 | public:
317 | tcp_sink_st(std::string server_host, int server_port, bool lazy_connect)
318 | {
319 | struct spdlog::sinks::tcp_sink_config tcp_config(server_host, server_port);
320 | tcp_config.lazy_connect = lazy_connect;
321 |
322 | _sink = std::make_shared(tcp_config);
323 | }
324 | };
325 |
326 | class tcp_sink_mt : public Sink {
327 | public:
328 | tcp_sink_mt(std::string server_host, int server_port, bool lazy_connect)
329 | {
330 | struct spdlog::sinks::tcp_sink_config tcp_config(server_host, server_port);
331 | tcp_config.lazy_connect = lazy_connect;
332 |
333 | _sink = std::make_shared(tcp_config);
334 | }
335 | };
336 |
337 | #ifdef SPDLOG_ENABLE_SYSLOG
338 | class syslog_sink_st : public Sink {
339 | public:
340 | syslog_sink_st(const std::string& ident = "", int syslog_option = 0, int syslog_facility = (1 << 3), bool enable_formatting = true)
341 | {
342 | _sink = std::make_shared(ident, syslog_option, syslog_facility, enable_formatting);
343 | }
344 | };
345 |
346 | class syslog_sink_mt : public Sink {
347 | public:
348 | syslog_sink_mt(const std::string& ident = "", int syslog_option = 0, int syslog_facility = (1 << 3), bool enable_formatting = true)
349 | {
350 | _sink = std::make_shared(ident, syslog_option, syslog_facility, enable_formatting);
351 | }
352 | };
353 | #endif
354 |
355 | class Logger {
356 | public:
357 | using async_factory_nb = spdlog::async_factory_impl;
358 |
359 | Logger(const std::string& name, bool async_mode)
360 | : _name(name)
361 | , _async(async_mode)
362 | {
363 | register_logger(name, this);
364 | }
365 |
366 | virtual ~Logger() {}
367 | std::string name() const
368 | {
369 | if (_logger)
370 | return _logger->name();
371 | else
372 | return "NULL";
373 | }
374 | void log(int level, const std::string& msg) const { this->_logger->log((spd::level::level_enum)level, msg); }
375 | void trace(const std::string& msg) const { this->_logger->trace(msg); }
376 | void debug(const std::string& msg) const { this->_logger->debug(msg); }
377 | void info(const std::string& msg) const { this->_logger->info(msg); }
378 | void warn(const std::string& msg) const { this->_logger->warn(msg); }
379 | void error(const std::string& msg) const { this->_logger->error(msg); }
380 | void critical(const std::string& msg) const { this->_logger->critical(msg); }
381 |
382 | bool should_log(int level) const
383 | {
384 | return _logger->should_log((spd::level::level_enum)level);
385 | }
386 |
387 | void set_level(int level)
388 | {
389 | _logger->set_level((spd::level::level_enum)level);
390 | }
391 |
392 | int level() const
393 | {
394 | return (int)_logger->level();
395 | }
396 |
397 | void set_pattern(const std::string& pattern, spd::pattern_time_type type = spd::pattern_time_type::local)
398 | {
399 | _logger->set_pattern(pattern, type);
400 | }
401 |
402 | // automatically call flush() if message level >= log_level
403 | void flush_on(int log_level)
404 | {
405 | _logger->flush_on((spd::level::level_enum)log_level);
406 | }
407 |
408 | void flush()
409 | {
410 | _logger->flush();
411 | }
412 |
413 | bool async()
414 | {
415 | return _async;
416 | }
417 |
418 | void close()
419 | {
420 | remove_logger(_name);
421 | _logger = nullptr;
422 | spdlog::drop(_name);
423 | }
424 |
425 | std::vector sinks() const
426 | {
427 | std::vector snks;
428 | for (const spd::sink_ptr& sink : _logger->sinks()) {
429 | snks.push_back(Sink(sink));
430 | }
431 | return snks;
432 | }
433 |
434 | void set_error_handler(spd::err_handler handler)
435 | {
436 | _logger->set_error_handler(handler);
437 | }
438 |
439 | std::shared_ptr get_underlying_logger() {
440 | return _logger;
441 | }
442 |
443 | protected:
444 | const std::string _name;
445 | bool _async;
446 | std::shared_ptr _logger{ nullptr };
447 | };
448 |
449 | class ConsoleLogger : public Logger {
450 | public:
451 | ConsoleLogger(const std::string& logger_name, bool multithreaded, bool standard_out, bool colored, bool async_mode = g_async_mode_on)
452 | : Logger(logger_name, async_mode)
453 | {
454 | if (standard_out) {
455 | if (multithreaded) {
456 | if (colored) {
457 | if (async_mode) {
458 | if (g_async_overflow_policy == spdlog::async_overflow_policy::overrun_oldest) {
459 | _logger = spd::stdout_color_mt(logger_name);
460 | } else {
461 | _logger = spd::stdout_color_mt(logger_name);
462 | }
463 | } else {
464 | _logger = spd::stdout_color_mt(logger_name);
465 | }
466 | } else {
467 | if (async_mode) {
468 | if (g_async_overflow_policy == spdlog::async_overflow_policy::overrun_oldest) {
469 | _logger = spd::stdout_logger_mt(logger_name);
470 | } else {
471 | _logger = spd::stdout_logger_mt(logger_name);
472 | }
473 | } else {
474 | _logger = spd::stdout_logger_mt(logger_name);
475 | }
476 | }
477 | } else {
478 | if (colored) {
479 | if (async_mode) {
480 | if (g_async_overflow_policy == spdlog::async_overflow_policy::overrun_oldest) {
481 | _logger = spd::stdout_color_st(logger_name);
482 | } else {
483 | _logger = spd::stdout_color_st(logger_name);
484 | }
485 | } else {
486 | _logger = spd::stdout_color_st(logger_name);
487 | }
488 | } else {
489 | if (async_mode) {
490 | if (g_async_overflow_policy == spdlog::async_overflow_policy::overrun_oldest) {
491 | _logger = spd::stdout_logger_st(logger_name);
492 | } else {
493 | _logger = spd::stdout_logger_st(logger_name);
494 | }
495 | } else {
496 | _logger = spd::stdout_logger_st(logger_name);
497 | }
498 | }
499 | }
500 |
501 | } else {
502 | if (multithreaded) {
503 | if (colored) {
504 | if (async_mode) {
505 | if (g_async_overflow_policy == spdlog::async_overflow_policy::overrun_oldest) {
506 | _logger = spd::stderr_color_mt(logger_name);
507 | } else {
508 | _logger = spd::stderr_color_mt(logger_name);
509 | }
510 | } else {
511 | _logger = spd::stderr_color_mt(logger_name);
512 | }
513 | } else {
514 | if (async_mode) {
515 | if (g_async_overflow_policy == spdlog::async_overflow_policy::overrun_oldest) {
516 | _logger = spd::stderr_logger_mt(logger_name);
517 | } else {
518 | _logger = spd::stderr_logger_mt(logger_name);
519 | }
520 | } else {
521 | _logger = spd::stderr_logger_mt(logger_name);
522 | }
523 | }
524 | } else {
525 | if (colored) {
526 | if (async_mode) {
527 | if (g_async_overflow_policy == spdlog::async_overflow_policy::overrun_oldest) {
528 | _logger = spd::stderr_color_st(logger_name);
529 | } else {
530 | _logger = spd::stderr_color_st(logger_name);
531 | }
532 | } else {
533 | _logger = spd::stderr_color_st(logger_name);
534 | }
535 | } else {
536 | if (async_mode) {
537 | if (g_async_overflow_policy == spdlog::async_overflow_policy::overrun_oldest) {
538 | _logger = spd::stderr_logger_st(logger_name);
539 | } else {
540 | _logger = spd::stderr_logger_st(logger_name);
541 | }
542 | } else {
543 | _logger = spd::stderr_logger_st(logger_name);
544 | }
545 | }
546 | }
547 | }
548 | }
549 | };
550 |
551 | class FileLogger : public Logger {
552 | public:
553 | FileLogger(const std::string& logger_name, const std::string& filename, bool multithreaded, bool truncate = false, bool async_mode = g_async_mode_on)
554 | : Logger(logger_name, async_mode)
555 | {
556 | if (multithreaded) {
557 | if (async_mode) {
558 | if (g_async_overflow_policy == spdlog::async_overflow_policy::overrun_oldest) {
559 | _logger = spd::basic_logger_mt(logger_name, filename, truncate);
560 | } else {
561 | _logger = spd::basic_logger_mt(logger_name, filename, truncate);
562 | }
563 | } else {
564 | _logger = spd::basic_logger_mt(logger_name, filename, truncate);
565 | }
566 | } else {
567 | if (async_mode) {
568 | if (g_async_overflow_policy == spdlog::async_overflow_policy::overrun_oldest) {
569 | _logger = spd::basic_logger_st(logger_name, filename, truncate);
570 | } else {
571 | _logger = spd::basic_logger_st(logger_name, filename, truncate);
572 | }
573 | } else {
574 | _logger = spd::basic_logger_st(logger_name, filename, truncate);
575 | }
576 | }
577 | }
578 | };
579 |
580 | class RotatingLogger : public Logger {
581 | public:
582 | RotatingLogger(const std::string& logger_name, const std::string& filename, bool multithreaded, size_t max_file_size, size_t max_files, bool async_mode = g_async_mode_on)
583 | : Logger(logger_name, async_mode)
584 | {
585 | if (multithreaded) {
586 | if (async_mode) {
587 | if (g_async_overflow_policy == spdlog::async_overflow_policy::overrun_oldest) {
588 | _logger = spd::rotating_logger_mt(logger_name, filename, max_file_size, max_files);
589 | } else {
590 | _logger = spd::rotating_logger_mt(logger_name, filename, max_file_size, max_files);
591 | }
592 | } else {
593 | _logger = spd::rotating_logger_mt(logger_name, filename, max_file_size, max_files);
594 | }
595 | } else {
596 | if (async_mode) {
597 | if (g_async_overflow_policy == spdlog::async_overflow_policy::overrun_oldest) {
598 | _logger = spd::rotating_logger_st(logger_name, filename, max_file_size, max_files);
599 | } else {
600 | _logger = spd::rotating_logger_st(logger_name, filename, max_file_size, max_files);
601 | }
602 | } else {
603 | _logger = spd::rotating_logger_st(logger_name, filename, max_file_size, max_files);
604 | }
605 | }
606 | }
607 | };
608 |
609 | class DailyLogger : public Logger {
610 | public:
611 | DailyLogger(const std::string& logger_name, const std::string& filename, bool multithreaded = false, int hour = 0, int minute = 0, bool async_mode = g_async_mode_on)
612 | : Logger(logger_name, async_mode)
613 | {
614 | if (multithreaded) {
615 | if (async_mode) {
616 | if (g_async_overflow_policy == spdlog::async_overflow_policy::overrun_oldest) {
617 | _logger = spd::daily_logger_mt(logger_name, filename, hour, minute);
618 | } else {
619 | _logger = spd::daily_logger_mt(logger_name, filename, hour, minute);
620 | }
621 | } else {
622 | _logger = spd::daily_logger_mt(logger_name, filename, hour, minute);
623 | }
624 | } else {
625 | if (async_mode) {
626 | if (g_async_overflow_policy == spdlog::async_overflow_policy::overrun_oldest) {
627 | _logger = spd::daily_logger_st(logger_name, filename, hour, minute);
628 | } else {
629 | _logger = spd::daily_logger_st(logger_name, filename, hour, minute);
630 | }
631 | } else {
632 | _logger = spd::daily_logger_st(logger_name, filename, hour, minute);
633 | }
634 | }
635 | }
636 | };
637 |
638 | #ifdef SPDLOG_ENABLE_SYSLOG
639 | class SyslogLogger : public Logger {
640 | public:
641 | SyslogLogger(const std::string& logger_name, bool multithreaded = false, const std::string& ident = "", int syslog_option = 0, int syslog_facilty = (1 << 3), bool async_mode = g_async_mode_on)
642 | : Logger(logger_name, async_mode)
643 | {
644 | if (multithreaded) {
645 | if (async_mode) {
646 | if (g_async_overflow_policy == spdlog::async_overflow_policy::overrun_oldest) {
647 | _logger = spd::syslog_logger_mt(logger_name, ident, syslog_option, syslog_facilty);
648 | } else {
649 | _logger = spd::syslog_logger_mt(logger_name, ident, syslog_option, syslog_facilty);
650 | }
651 | } else {
652 | _logger = spd::syslog_logger_mt(logger_name, ident, syslog_option, syslog_facilty);
653 | }
654 | } else {
655 | if (async_mode) {
656 | if (g_async_overflow_policy == spdlog::async_overflow_policy::overrun_oldest) {
657 | _logger = spd::syslog_logger_st(logger_name, ident, syslog_option, syslog_facilty);
658 | } else {
659 | _logger = spd::syslog_logger_st(logger_name, ident, syslog_option, syslog_facilty);
660 | }
661 | } else {
662 | _logger = spd::syslog_logger_st(logger_name, ident, syslog_option, syslog_facilty);
663 | }
664 | }
665 | }
666 | };
667 | #endif
668 |
669 | class AsyncOverflowPolicy {
670 | public:
671 | const static int block{ (int)spd::async_overflow_policy::block };
672 | const static int overrun_oldest{ (int)spd::async_overflow_policy::overrun_oldest };
673 | };
674 |
675 | void set_async_mode(size_t queue_size = spdlog::details::default_async_q_size, size_t thread_count = 1, int async_overflow_policy = AsyncOverflowPolicy::block) {
676 | // Initialize/replace the global spdlog thread pool.
677 | auto& registry = spdlog::details::registry::instance();
678 | std::lock_guard tp_lck(registry.tp_mutex());
679 | auto tp = std::make_shared(queue_size, thread_count);
680 | registry.set_tp(tp);
681 |
682 | g_async_overflow_policy = static_cast(async_overflow_policy);
683 | g_async_mode_on = true;
684 | }
685 |
686 | std::shared_ptr thread_pool() {
687 | auto& registry = spdlog::details::registry::instance();
688 | std::lock_guard tp_lck(registry.tp_mutex());
689 | auto tp = registry.get_tp();
690 | if(tp == nullptr) {
691 | set_async_mode();
692 | auto tp = registry.get_tp();
693 | }
694 |
695 | return tp;
696 | }
697 |
698 | class SinkLogger : public Logger {
699 | public:
700 | SinkLogger(const std::string& logger_name, const Sink& sink, bool async_mode = g_async_mode_on)
701 | : Logger(logger_name, async_mode)
702 | {
703 | if (async_mode) {
704 | _logger = std::shared_ptr(new spd::async_logger(logger_name, sink.get_sink(), thread_pool(), g_async_overflow_policy));
705 | } else {
706 | _logger = std::shared_ptr(new spd::logger(logger_name, sink.get_sink()));
707 | }
708 | }
709 | SinkLogger(const std::string& logger_name, const std::vector& sink_list, bool async_mode = g_async_mode_on)
710 | : Logger(logger_name, async_mode)
711 | {
712 | std::vector sinks;
713 | for (auto sink : sink_list)
714 | sinks.push_back(sink.get_sink());
715 |
716 | if (async_mode) {
717 | _logger = std::shared_ptr(new spd::async_logger(logger_name, sinks.begin(), sinks.end(), thread_pool(), g_async_overflow_policy));
718 | } else {
719 | _logger = std::shared_ptr(new spd::logger(logger_name, sinks.begin(), sinks.end()));
720 | }
721 | }
722 | };
723 |
724 | Logger get(const std::string& name)
725 | {
726 | Logger* logger = access_logger(name);
727 | if (logger)
728 | return *logger;
729 | else
730 | throw std::runtime_error(std::string("Logger name: " + name + " could not be found"));
731 | }
732 |
733 | void drop(const std::string& name)
734 | {
735 | remove_logger(name);
736 | spdlog::drop(name);
737 | }
738 |
739 | void drop_all()
740 | {
741 | remove_logger_all();
742 | spdlog::drop_all();
743 | }
744 |
745 | }
746 |
747 | PYBIND11_MODULE(spdlog, m)
748 | {
749 | m.doc() = R"pbdoc(
750 | spdlog module
751 | -----------------------
752 |
753 | .. currentmodule:: spdlog
754 |
755 | .. autosummary::
756 | :toctree: _generate
757 |
758 | LogLevel
759 | Logger
760 | )pbdoc";
761 |
762 | py::class_>(m, "_spd_logger");
763 |
764 | m.def("set_async_mode", set_async_mode,
765 | py::arg("queue_size") = 1 << 16,
766 | py::arg("thread_count") = 1,
767 | py::arg("overflow_policy") = 0);
768 |
769 | py::class_(m, "Sink")
770 | .def(py::init<>())
771 | .def("set_level", &Sink::set_level);
772 |
773 | py::class_(m, "stdout_sink_st")
774 | .def(py::init<>());
775 |
776 | py::class_(m, "stdout_sink_mt")
777 | .def(py::init<>());
778 |
779 | py::class_(m, "stdout_color_sink_st")
780 | .def(py::init<>());
781 |
782 | py::class_(m, "stdout_color_sink_mt")
783 | .def(py::init<>());
784 |
785 | py::class_(m, "stderr_sink_st")
786 | .def(py::init<>());
787 |
788 | py::class_(m, "stderr_sink_mt")
789 | .def(py::init<>());
790 |
791 | py::class_(m, "stderr_color_sink_st")
792 | .def(py::init<>());
793 |
794 | py::class_(m, "stderr_color_sink_mt")
795 | .def(py::init<>());
796 |
797 | py::class_(m, "basic_file_sink_st")
798 | .def(py::init(), py::arg("filename"), py::arg("truncate") = false);
799 |
800 | py::class_(m, "basic_file_sink_mt")
801 | .def(py::init(), py::arg("filename"), py::arg("truncate") = false);
802 |
803 | py::class_(m, "daily_file_sink_st")
804 | .def(py::init(), py::arg("filename"),
805 | py::arg("rotation_hour"),
806 | py::arg("rotation_minute"));
807 |
808 | py::class_(m, "daily_file_sink_mt")
809 | .def(py::init(), py::arg("filename"),
810 | py::arg("rotation_hour"),
811 | py::arg("rotation_minute"));
812 |
813 | py::class_(m, "rotating_file_sink_st")
814 | .def(py::init(), py::arg("filename"),
815 | py::arg("max_size"),
816 | py::arg("max_files"));
817 |
818 | py::class_(m, "rotating_file_sink_mt")
819 | .def(py::init(), py::arg("filename"),
820 | py::arg("max_size"),
821 | py::arg("max_files"));
822 |
823 | py::class_(m, "dist_sink_mt")
824 | .def(py::init<>())
825 | .def(py::init>(), py::arg("sinks"))
826 | .def("add_sink", &dist_sink_mt::add_sink, py::arg("sink"))
827 | .def("remove_sink", &dist_sink_mt::remove_sink, py::arg("sink"))
828 | .def("set_sinks", &dist_sink_mt::set_sinks, py::arg("sinks"))
829 | .def("sinks", &dist_sink_mt::sinks);
830 |
831 | py::class_(m, "dist_sink_st")
832 | .def(py::init<>())
833 | .def(py::init>(), py::arg("sinks"))
834 | .def("add_sink", &dist_sink_st::add_sink, py::arg("sink"))
835 | .def("remove_sink", &dist_sink_st::remove_sink, py::arg("sink"))
836 | .def("set_sinks", &dist_sink_st::set_sinks, py::arg("sinks"))
837 | .def("sinks", &dist_sink_st::sinks);
838 |
839 | py::class_(m, "dup_filter_sink_st")
840 | .def(py::init(), py::arg("max_skip_duration_seconds"));
841 |
842 | py::class_(m, "dup_filter_sink_mt")
843 | .def(py::init(), py::arg("max_skip_duration_seconds"));
844 |
845 | py::class_(m, "null_sink_st")
846 | .def(py::init<>());
847 |
848 | py::class_(m, "null_sink_mt")
849 | .def(py::init<>());
850 |
851 | py::class_(m, "tcp_sink_st")
852 | .def(py::init(),
853 | py::arg("server_host"),
854 | py::arg("server_port"),
855 | py::arg("lazy_connect"));
856 |
857 | py::class_(m, "tcp_sink_mt")
858 | .def(py::init(),
859 | py::arg("server_host"),
860 | py::arg("server_port"),
861 | py::arg("lazy_connect"));
862 |
863 | py::class_(m, "LogLevel")
864 | .def_property_readonly_static("TRACE", [](py::object) { return LogLevel::trace; })
865 | .def_property_readonly_static("DEBUG", [](py::object) { return LogLevel::debug; })
866 | .def_property_readonly_static("INFO", [](py::object) { return LogLevel::info; })
867 | .def_property_readonly_static("WARN", [](py::object) { return LogLevel::warn; })
868 | .def_property_readonly_static("ERR", [](py::object) { return LogLevel::err; })
869 | .def_property_readonly_static("CRITICAL", [](py::object) { return LogLevel::critical; })
870 | .def_property_readonly_static("OFF", [](py::object) { return LogLevel::off; });
871 |
872 | py::class_(m, "AsyncOverflowPolicy")
873 | .def_property_readonly_static("BLOCK", [](py::object) { return AsyncOverflowPolicy::block; })
874 | .def_property_readonly_static("OVERRUN_OLDEST", [](py::object) { return AsyncOverflowPolicy::overrun_oldest; });
875 |
876 | py::enum_(m, "PatternTimeType")
877 | .value("local", spdlog::pattern_time_type::local)
878 | .value("utc", spdlog::pattern_time_type::utc)
879 | .export_values();
880 |
881 | py::class_(m, "Logger")
882 | .def("log", &Logger::log)
883 | .def("trace", &Logger::trace)
884 | .def("debug", &Logger::debug)
885 | .def("info", &Logger::info)
886 | .def("warn", &Logger::warn)
887 | .def("error", &Logger::error)
888 | .def("critical", &Logger::critical)
889 | .def("name", &Logger::name)
890 | .def("should_log", &Logger::should_log)
891 | .def("set_level", &Logger::set_level)
892 | .def("level", &Logger::level)
893 | .def("set_pattern", &Logger::set_pattern,
894 | py::arg("pattern"), py::arg("type") = spd::pattern_time_type::local, "type refers to time format and takes 'local' or 'utc'")
895 | .def("flush_on", &Logger::flush_on)
896 | .def("flush", &Logger::flush)
897 | .def("close", &Logger::close)
898 | .def("async_mode", &Logger::async)
899 | .def("sinks", &Logger::sinks)
900 | .def("set_error_handler", &Logger::set_error_handler)
901 | .def("get_underlying_logger", &Logger::get_underlying_logger);
902 |
903 | py::class_(m, "SinkLogger")
904 | .def(py::init&>(),
905 | py::arg("name"),
906 | py::arg("sinks"))
907 | .def(py::init&, bool>(),
908 | py::arg("name"),
909 | py::arg("sinks"),
910 | py::arg("async_mode"));
911 |
912 | py::class_(m, "ConsoleLogger")
913 | .def(py::init(),
914 | py::arg("name"),
915 | py::arg("multithreaded") = false,
916 | py::arg("stdout") = true,
917 | py::arg("colored") = true)
918 | .def(py::init(),
919 | py::arg("name"),
920 | py::arg("multithreaded") = false,
921 | py::arg("stdout") = true,
922 | py::arg("colored") = true,
923 | py::arg("async_mode"));
924 |
925 | py::class_(m, "FileLogger")
926 | .def(py::init(),
927 | py::arg("name"),
928 | py::arg("filename"),
929 | py::arg("multithreaded") = false,
930 | py::arg("truncate") = false)
931 | .def(py::init(),
932 | py::arg("name"),
933 | py::arg("filename"),
934 | py::arg("multithreaded") = false,
935 | py::arg("truncate") = false,
936 | py::arg("async_mode"));
937 | py::class_(m, "RotatingLogger")
938 | .def(py::init(),
939 | py::arg("name"),
940 | py::arg("filename"),
941 | py::arg("multithreaded"),
942 | py::arg("max_file_size"),
943 | py::arg("max_files"))
944 | .def(py::init(),
945 | py::arg("name"),
946 | py::arg("filename"),
947 | py::arg("multithreaded"),
948 | py::arg("max_file_size"),
949 | py::arg("max_files"),
950 | py::arg("async_mode"));
951 | py::class_(m, "DailyLogger")
952 | .def(py::init(),
953 | py::arg("name"),
954 | py::arg("filename"),
955 | py::arg("multithreaded") = false,
956 | py::arg("hour") = 0,
957 | py::arg("minute") = 0)
958 | .def(py::init(),
959 | py::arg("name"),
960 | py::arg("filename"),
961 | py::arg("multithreaded") = false,
962 | py::arg("hour") = 0,
963 | py::arg("minute") = 0,
964 | py::arg("async_mode"));
965 |
966 | //SyslogLogger(const std::string& logger_name, const std::string& ident = "", int syslog_option = 0, int syslog_facilty = (1<<3))
967 | #ifdef SPDLOG_ENABLE_SYSLOG
968 | py::class_(m, "syslog_sink_st")
969 | .def(py::init(),
970 | py::arg("ident") = "",
971 | py::arg("syslog_option") = 0,
972 | py::arg("syslog_facility") = (1 << 3),
973 | py::arg("enable_formatting") = true);
974 | py::class_(m, "syslog_sink_mt")
975 | .def(py::init(),
976 | py::arg("ident") = "",
977 | py::arg("syslog_option") = 0,
978 | py::arg("syslog_facility") = (1 << 3),
979 | py::arg("enable_formatting") = true);
980 | py::class_(m, "SyslogLogger")
981 | .def(py::init(),
982 | py::arg("name"),
983 | py::arg("multithreaded") = false,
984 | py::arg("ident") = "",
985 | py::arg("syslog_option") = 0,
986 | py::arg("syslog_facility") = (1 << 3))
987 | .def(py::init(),
988 | py::arg("name"),
989 | py::arg("multithreaded") = false,
990 | py::arg("ident") = "",
991 | py::arg("syslog_option") = 0,
992 | py::arg("syslog_facility") = (1 << 3),
993 | py::arg("async_mode"));
994 | #endif
995 | m.def("get", get, py::arg("name"), py::return_value_policy::copy);
996 | m.def("drop", drop, py::arg("name"));
997 | m.def("drop_all", drop_all);
998 |
999 | #ifdef VERSION_INFO
1000 | m.attr("__version__") = VERSION_INFO;
1001 | #else
1002 | m.attr("__version__") = "dev";
1003 | #endif
1004 | }
1005 |
--------------------------------------------------------------------------------
/tests/sink_test.py:
--------------------------------------------------------------------------------
1 | import spdlog
2 | import time
3 | import os
4 |
5 | sinks = [
6 | spdlog.stdout_sink_st(),
7 | spdlog.stdout_sink_mt(),
8 | spdlog.stderr_sink_st(),
9 | spdlog.stderr_sink_mt(),
10 | spdlog.daily_file_sink_st("DailySinkSt.log", 0, 0),
11 | spdlog.daily_file_sink_mt("DailySinkMt.log", 0, 0),
12 | spdlog.rotating_file_sink_st("RotSt.log", 1024, 1024),
13 | spdlog.rotating_file_sink_mt("RotMt.log", 1024, 1024),
14 | ]
15 |
16 |
17 | logger = spdlog.SinkLogger("Hello", sinks)
18 | logger.info("Kukucs")
19 | logger.info("Alma")
--------------------------------------------------------------------------------
/tests/spdlog_vs_logging.py:
--------------------------------------------------------------------------------
1 | import spdlog
2 | import logging
3 | import time
4 | import statistics
5 | import random
6 | import numpy as np
7 | from functools import partial
8 |
9 | MICROSEC_IN_SEC = 1e6
10 |
11 | def timed(func):
12 | def wrapper(*args):
13 | start = time.perf_counter()
14 | func(*args)
15 | return (time.perf_counter() - start) * MICROSEC_IN_SEC
16 | return wrapper
17 |
18 |
19 | def generate_message(msg_len):
20 | msg = ""
21 | for i in range(msg_len):
22 | c = random.randint(ord('a'), ord('z'))
23 | msg += chr(c)
24 | return msg
25 |
26 | def generate_numpy_array(array_len):
27 | return np.random.rand(array_len)
28 |
29 | def generate_numpy_array_str(array_len):
30 | return f'{np.random.rand(array_len)}'
31 |
32 |
33 |
34 | @timed
35 | def do_logging(logger, message, count):
36 | for i in range(count):
37 | logger.info(message)
38 |
39 |
40 |
41 | def build_timings_per_len(message_lengths):
42 | timings = {"spdlog" : {}, "logging" : {}}
43 | for msg_len in message_lengths:
44 | timings["spdlog"][msg_len] = []
45 | timings["logging"][msg_len] = []
46 | return timings
47 |
48 |
49 | def candidate_logger(logger, name, epochs, sub_epochs,repeat_cnt, message_lengths, message_generator, worker, timings):
50 | for epoch in range(epochs):
51 | for msg_len in message_lengths:
52 | msg = message_generator(msg_len)
53 | for _ in range(sub_epochs):
54 | took = logger(msg, repeat_cnt)
55 | timings[name][msg_len].append(took / repeat_cnt)
56 | worker()
57 |
58 | def lets_do_some_work():
59 | x = [i for i in range(1 << 5)]
60 | y = [i for i in range(1 << 5)]
61 | result = []
62 | for i, j in zip(x,y):
63 | z = x + y
64 | result.append(z)
65 |
66 |
67 | def mode(data):
68 | data = sorted(data)
69 | return data[len(data)//2]
70 |
71 | def generate_stats(timings):
72 | d = {"spdlog": {}, "logging" : {}}
73 | for logger, time_per_msg_len in timings.items():
74 | for msg_len, times in time_per_msg_len.items():
75 | mean = statistics.mean(times)
76 | mo = mode(times)
77 | stddev = statistics.stdev(times)
78 | m = max(times)
79 | d[logger][msg_len] = {"mean": mean, "mode" : mo,
80 | "stddev" : stddev, "max" : m}
81 | return d
82 |
83 | def calculate_ratio(timings, logger1, logger2):
84 | t1,t2 = timings[logger1], timings[logger2]
85 | msg_lens = t1.keys()
86 | return { ml : t1[ml]["mean"]/t2[ml]["mean"] for ml in msg_lens }
87 |
88 |
89 | def print_stats(timings):
90 | for logger in timings.keys():
91 | print("Logger: ", logger)
92 | for msg_len in timings[logger].keys():
93 | t = timings[logger][msg_len]["mean"]
94 | print(msg_len, " - ", t)
95 |
96 |
97 | def run_test(async_mode):
98 |
99 | message_lengths = [10, 20, 40, 100, 300, 1000, 5000, 20000]
100 | repeat_cnt = 5
101 | epochs = 20
102 | sub_epochs = 10
103 | if async_mode:
104 | spdlog.set_async_mode(queue_size=1 << 24)
105 |
106 | spd_logger = spdlog.FileLogger(name='speedlogger', filename='speedlog.log', multithreaded=False, truncate=False)
107 | if spd_logger.async_mode() != async_mode:
108 | print(f"spdlog should be in {async_mode} mode but is in {spd_logger.async_mode()}")
109 |
110 | standard_logger = logging.getLogger('logging')
111 | fh = logging.FileHandler('logging.log')
112 | fh.setLevel(logging.DEBUG)
113 | standard_logger.addHandler(fh)
114 | standard_logger.setLevel(logging.DEBUG)
115 |
116 | timings = build_timings_per_len(message_lengths)
117 |
118 | candidate_logger(partial(do_logging, spd_logger), 'spdlog', epochs, sub_epochs, repeat_cnt, message_lengths, generate_message, lets_do_some_work, timings)
119 | candidate_logger(partial(do_logging, standard_logger), 'logging', epochs,sub_epochs, repeat_cnt, message_lengths, generate_message, lets_do_some_work, timings)
120 |
121 |
122 | final = generate_stats(timings)
123 | print("Message len -> time microsec")
124 | #print(final)
125 |
126 | print_stats(final)
127 | ratios = calculate_ratio(final, 'spdlog', 'logging')
128 |
129 | for msg_len, ratio in ratios.items():
130 | print(f"spdlog takes {ratio * 100}% of logging at message len: {msg_len}")
131 |
132 | if async_mode:
133 | sleeptime = 4
134 | print(f"Sleeping for secs: {sleeptime}")
135 | time.sleep(sleeptime)
136 | spd_logger.close()
137 |
138 |
139 | if __name__ == "__main__":
140 | print("Running in spdlog in sync mode")
141 | run_test(False)
142 | print("Running in spdlog in async mode")
143 | run_test(True)
144 |
145 |
146 |
147 |
148 |
--------------------------------------------------------------------------------
/tests/test_spdlog.py:
--------------------------------------------------------------------------------
1 | import spdlog
2 | import unittest
3 |
4 | from spdlog import ConsoleLogger, FileLogger, RotatingLogger, DailyLogger, LogLevel
5 |
6 | def set_log_level(logger, level):
7 | print("Setting Log level to %d" % level)
8 | logger.set_level(level)
9 |
10 |
11 | def log_msg(logger):
12 | logger.trace('I am Trace')
13 | logger.debug('I am Debug')
14 | logger.info('I am Info')
15 | logger.warn('I am Warning')
16 | logger.error('I am Error')
17 | logger.critical('I am Critical')
18 |
19 |
20 | class SpdLogTest(unittest.TestCase):
21 | def test_console_logger(self):
22 | name = 'Console Logger'
23 | tf = (True, False)
24 | for multithreaded in tf:
25 | for stdout in tf:
26 | for colored in tf:
27 | logger = ConsoleLogger(name, multithreaded, stdout, colored)
28 | logger.info('I am a console log test.')
29 | spdlog.drop(name)
30 |
31 | def test_drop(self):
32 | name = 'Console Logger'
33 | for i in range(10):
34 | tf = (True, False)
35 | for multithreaded in tf:
36 | for stdout in tf:
37 | for colored in tf:
38 | logger = ConsoleLogger(name, multithreaded, stdout, colored)
39 | spdlog.drop(logger.name())
40 | def test_log_level(self):
41 | logger = ConsoleLogger('Logger', False, True, True)
42 | for level in (LogLevel.TRACE, LogLevel.DEBUG, LogLevel.INFO, LogLevel.WARN,
43 | LogLevel.ERR, LogLevel.CRITICAL):
44 | set_log_level(logger, level)
45 | log_msg(logger)
46 |
47 |
48 |
49 | if __name__ == "__main__":
50 | unittest.main()
51 |
52 |
--------------------------------------------------------------------------------
/upload_to_pypi.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | twine upload dist/*
3 |
--------------------------------------------------------------------------------