Skip to content

Commit 29255a7

Browse files
committed
WIP
1 parent c8cc3ac commit 29255a7

File tree

11 files changed

+166
-82
lines changed

11 files changed

+166
-82
lines changed

.github/workflows/unix.yml

+16-4
Original file line numberDiff line numberDiff line change
@@ -82,11 +82,16 @@ jobs:
8282
DEBIAN_FRONTEND: noninteractive
8383
UBSAN_OPTIONS: halt_on_error=1:abort_on_error=1
8484
steps:
85-
- uses: actions/checkout@v2
85+
- uses: actions/checkout@v4
8686

8787
- uses: seanmiddleditch/gha-setup-ninja@master
8888
if: matrix.config.os == 'macos-latest'
8989

90+
- name: Install doxygen 🔧
91+
uses: ssciwr/doxygen-install@v1
92+
with:
93+
version: "1.13.2"
94+
9095
# ==== INSTALL ====
9196
- name: Update APT
9297
if: matrix.config.os != 'macos-latest'
@@ -141,21 +146,28 @@ jobs:
141146
steps:
142147
- name: Checkout 🛎️
143148
uses: actions/checkout@v4
149+
144150
- name: Set up Python 3.13 🔧
145151
uses: actions/setup-python@v5
146152
with:
147153
python-version: 3.13
154+
148155
- name: Install doxygen 🔧
149156
uses: ssciwr/doxygen-install@v1
150157
with:
151158
version: "1.13.2"
159+
152160
- name: Install Python dependencies ⚙️
153161
run: pip install -r docs/requirements.txt
162+
154163
- name: Generate HTML documentation 🏗️
155164
run: |
156-
mkdir -p public/
157-
158-
- name: Deploy documentation 🚀
165+
cd docs
166+
doxygen
167+
sphinx-build -M html . _build
168+
mv _build ../public
169+
170+
- name: Deploy documentation onto GitHub Pages 🚀
159171
if: github.ref == 'refs/heads/master'
160172
uses: peaceiris/actions-gh-pages@v3
161173
with:

.github/workflows/windows.yml

+8-1
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,15 @@ jobs:
3131
cmake-opt: -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl
3232

3333
steps:
34-
- uses: actions/checkout@v2
34+
- uses: actions/checkout@v4
35+
3536
- uses: seanmiddleditch/gha-setup-ninja@master
37+
38+
- name: Install doxygen 🔧
39+
uses: ssciwr/doxygen-install@v1
40+
with:
41+
version: "1.13.2"
42+
3643
- uses: ilammy/msvc-dev-cmd@v1
3744

3845
- name: Build

CMakeLists.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ file(GLOB_RECURSE utl-files src/*.cc)
1313
add_library(utl STATIC ${utl-files})
1414
target_link_libraries(utl PUBLIC fmt-header-only cista)
1515
target_include_directories(utl PUBLIC include)
16-
target_compile_options(utl PUBLIC -D_SCL_SECURE_NO_WARNINGS=1)
16+
target_compile_options(utl PUBLIC -D_SCL_SECURE_NO_WARNINGS=1 -Wno-invalid-token-paste)
1717
target_compile_features(utl PUBLIC cxx_std_23)
1818
if (MSVC)
1919
target_compile_definitions(utl PUBLIC NOMINMAX)

docs/Doxyfile

+1-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ DOXYFILE_ENCODING = UTF-8
4141
# project for which the documentation is generated. This name is used in the
4242
# title of most generated pages and in a few other places.
4343

44-
PROJECT_NAME = "Motis utl module"
44+
PROJECT_NAME = "MOTIS utl module"
4545

4646
# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
4747
# could be handy for archiving the generated documentation or if some version

docs/README.md

+6-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ Directly with `doxygen`:
1212
doxygen
1313

1414
## Generating Sphinx documentation
15-
From the root directory:
15+
From the root directory, with auto-reload:
1616

1717
sphinx-autobuild docs docs/_build/html --open-browser
18+
19+
Only generating the documentation:
20+
21+
cd docs
22+
sphinx-build -M html . _build

docs/conf.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@
66
# -- Project information -----------------------------------------------------
77
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
88

9-
project = 'Motis utl module'
10-
author = 'The Motis project developers'
11-
copyright = '2025, The Motis project developers'
9+
project = 'MOTIS utl module'
10+
author = 'The MOTIS project developers'
11+
copyright = '2025, The MOTIS project developers'
1212
html_show_copyright = False
1313

1414
# -- General configuration ---------------------------------------------------

docs/index.rst

+32-9
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
Motis utl module documentation
1+
MOTIS utl module documentation
22
==============================
33

44
MOTIS is an open-source software platform for efficient planning and routing in multi-modal transportation systems.
55
GitHub main repository: https://github.com/motis-project/motis
66

7-
This is the documentation for the `utl` (utility) module.
7+
This is the documentation for the **utl** (utility) module.
88

99
..
1010
Table of contents
@@ -17,12 +17,35 @@ This is the documentation for the `utl` (utility) module.
1717
Logging
1818
-------
1919

20-
Logs can be produced using the `log()` `struct`::
20+
The simplest way to produce log lines is to use the ``logF()`` macro::
2121

22-
utl::log() << "[" << utl::log::str["info"] << "]" \
23-
<< "[" << utl::time() << "]" \
24-
<< "[" << FILE_NAME << ":" << __LINE__ << "]" \
25-
<< " Some message"
22+
logF(info, "Simple message");
2623

27-
.. doxygenstruct:: utl::log
28-
:members:
24+
The following log levels are supported:
25+
26+
debug
27+
Messages that contain information only useful when debugging MOTIS
28+
29+
info
30+
Important information about a normal behavior of the program
31+
32+
error
33+
Details on an abnormal behavior of the application
34+
35+
Advanced usage
36+
^^^^^^^^^^^^^^
37+
38+
By default, ``logF()`` inserts the current filename & linenumber in the log line.
39+
However, you can use ``log()`` to specify your own **context** ::
40+
41+
log(info, "http.get.resource", "Details");
42+
43+
You can also insert variables in the message by using ``{}`` and passing them as extra arguments::
44+
45+
logF(info, "String={} Int={}", "Hello", 42);
46+
47+
API details
48+
^^^^^^^^^^^
49+
Under the hood, the ``log()`` & ``logF()`` macros use the ``utl::log()`` function:
50+
51+
.. doxygenfunction:: utl::log

include/utl/logging.h

+62-53
Original file line numberDiff line numberDiff line change
@@ -5,75 +5,84 @@
55
#else
66

77
#include <chrono>
8-
#include <cstring>
9-
#include <ctime>
8+
#include <iomanip>
109
#include <iostream>
11-
#include <mutex>
10+
#include <sstream>
1211
#include <string>
1312

14-
#ifdef _MSC_VER
15-
#define gmt(a, b) gmtime_s(b, a)
16-
#else
17-
#define gmt(a, b) gmtime_r(a, b)
18-
#endif
13+
#include "fmt/core.h"
14+
#include "fmt/ostream.h"
1915

20-
#define FILE_NAME \
21-
(strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
16+
#define STRINGIFY(x) STRINGIFY_(x)
17+
#define STRINGIFY_(x) #x
2218

23-
#define uLOG(lvl) \
24-
utl::log() << "[" << utl::log::str[lvl] << "]" \
25-
<< "[" << utl::time() << "]" \
26-
<< "[" << FILE_NAME << ":" << __LINE__ << "]" \
27-
<< " "
19+
#define FILE_AND_LINE (__FILE__ ":" STRINGIFY(__LINE__))
20+
#if defined(_WIN32)
21+
#define FILE_AND_LINE_SHORT \
22+
(strrchr(FILE_AND_LINE, '\\') ? strrchr(FILE_AND_LINE, '\\') + 1 \
23+
: FILE_AND_LINE)
24+
#else
25+
#define FILE_AND_LINE_SHORT \
26+
(strrchr(FILE_AND_LINE, '/') ? strrchr(FILE_AND_LINE, '/') + 1 \
27+
: FILE_AND_LINE)
28+
#endif
2829

2930
namespace utl {
3031

31-
/**
32-
Entrypoint for all Motis logs
33-
*/
34-
struct log {
35-
log() = default;
36-
37-
log(log const&) = delete;
38-
log& operator=(log const&) = delete;
39-
40-
log(log&&) = default;
41-
log& operator=(log&&) = default;
32+
enum class log_level { debug, info, error };
4233

43-
template <typename T>
44-
friend log&& operator<<(log&& l, T&& t) {
45-
std::clog << std::forward<T&&>(t);
46-
return std::move(l);
34+
constexpr char const* to_str(log_level const level) {
35+
switch (level) {
36+
case log_level::debug: return "debug";
37+
case log_level::info: return "info";
38+
case log_level::error: return "error";
4739
}
40+
return "";
41+
}
4842

49-
~log() { std::clog << std::endl; }
50-
51-
static constexpr const char* const str[]{"emrg", "alrt", "crit", "erro",
52-
"warn", "note", "info", "debg"};
53-
};
43+
static log_level s_verbosity;
5444

55-
enum log_level { emrg, alrt, crit, err, warn, notice, info, debug };
45+
inline std::string now() {
46+
using clock = std::chrono::system_clock;
47+
auto const now = clock::to_time_t(clock::now());
48+
struct tm tmp {};
49+
#if _MSC_VER >= 1400
50+
gmtime_s(&tmp, &now);
51+
#else
52+
gmtime_r(&now, &tmp);
53+
#endif
5654

57-
/**
58-
Format a timestamp as an ISO 8601 string
59-
*/
60-
inline std::string time(time_t const t) {
61-
char buf[sizeof "2011-10-08t07:07:09z-0430"];
62-
struct tm result {};
63-
gmt(&t, &result);
64-
strftime(buf, sizeof buf, "%FT%TZ%z", &result);
65-
return buf;
55+
std::stringstream ss;
56+
ss << std::put_time(&tmp, "%FT%TZ");
57+
return ss.str();
6658
}
6759

68-
/**
69-
Format the current time as an ISO 8601 string
70-
*/
71-
inline std::string time() {
72-
time_t now;
73-
std::time(&now);
74-
return time(now);
60+
/// Produce a new log line at the given `level`, with the given prefix `ctx` and
61+
/// message
62+
template <typename... Args>
63+
void log(log_level const level, char const* ctx,
64+
fmt::format_string<Args...> fmt_str, Args&&... args) {
65+
if (level >= ::utl::s_verbosity) {
66+
fmt::print(std::clog, "{time} [{level}] [{ctx}] {msg}\n",
67+
fmt::arg("time", now()), fmt::arg("level", to_str(level)),
68+
fmt::arg("ctx", ctx),
69+
fmt::arg("msg", fmt::format(fmt::runtime(fmt_str),
70+
std::forward<Args>(args)...)));
71+
}
7572
}
7673

7774
} // namespace utl
7875

79-
#endif
76+
/**
77+
* Shorthand to invoke utl::log without specifying the namespace
78+
*/
79+
#define log(level, ctx, fmt_str, ...) \
80+
utl::log(utl::log_level::##level, ctx, fmt_str, __VA_ARGS__)
81+
82+
/**
83+
* Invoke utl::log using the current C++ filename & line number as ctx
84+
*/
85+
#define logF(level, fmt_str, ...) \
86+
log(level, FILE_AND_LINE_SHORT, fmt_str, __VA_ARGS__)
87+
88+
#endif // LOGGING_HEADER

include/utl/parallel_for.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ inline errors_t parallel_for(
160160
jobs.size(),
161161
[&](auto const idx) {
162162
if (idx % mod == 0) {
163-
uLOG(info) << desc << " " << idx << "/" << jobs.size();
163+
logF(info, "{} {}/{}", desc, idx, jobs.size());
164164
}
165165
func(jobs[idx]);
166166
},

src/timer.cc

+6-8
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ namespace utl {
66

77
scoped_timer::scoped_timer(std::string name)
88
: name_{std::move(name)}, start_{std::chrono::steady_clock::now()} {
9-
uLOG(info) << "[" << name_ << "] starting";
9+
logF(info, "[{}] starting", name);
1010
}
1111

1212
scoped_timer::~scoped_timer() {
@@ -15,8 +15,7 @@ scoped_timer::~scoped_timer() {
1515
double t =
1616
static_cast<double>(duration_cast<microseconds>(stop - start_).count()) /
1717
1000.0;
18-
uLOG(info) << "[" << name_ << "] finished"
19-
<< " (" << t << "ms)";
18+
logF(info, "[{}] finished ({}ms)", name_, t);
2019
}
2120

2221
void scoped_timer::print(std::string_view const message) const {
@@ -25,12 +24,12 @@ void scoped_timer::print(std::string_view const message) const {
2524
double const t =
2625
static_cast<double>(duration_cast<microseconds>(stop - start_).count()) /
2726
1000.0;
28-
uLOG(info) << "[" << name_ << "] " << message << " (" << t << "ms)";
27+
logF(info, "[{}] {} ({}ms)", name_, message, t);
2928
}
3029

3130
manual_timer::manual_timer(std::string name)
3231
: name_{std::move(name)}, start_{std::chrono::steady_clock::now()} {
33-
uLOG(info) << "[" << name_ << "] starting";
32+
logF(info, "[{}] starting", name_);
3433
}
3534

3635
void manual_timer::stop_and_print() const {
@@ -39,8 +38,7 @@ void manual_timer::stop_and_print() const {
3938
double t =
4039
static_cast<double>(duration_cast<microseconds>(stop - start_).count()) /
4140
1000.0;
42-
uLOG(info) << "[" << name_ << "] finished"
43-
<< " (" << t << "ms)";
41+
logF(info, "[{}] finished ({}ms)", name_, t);
4442
}
4543

4644
void manual_timer::print(std::string_view const message) const {
@@ -49,7 +47,7 @@ void manual_timer::print(std::string_view const message) const {
4947
double const t =
5048
static_cast<double>(duration_cast<microseconds>(stop - start_).count()) /
5149
1000.0;
52-
uLOG(info) << "[" << name_ << "] " << message << " (" << t << "ms)";
50+
logF(info, "[{}] {} ({}ms)", name_, message, t);
5351
}
5452

5553
} // namespace utl

test/logging_test.cc

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#include <gmock/gmock.h>
2+
#include <gtest/gtest.h>
3+
4+
#include "utl/logging.h"
5+
6+
using ::testing::MatchesRegex;
7+
8+
TEST(log, basic_usage) {
9+
testing::internal::CaptureStderr();
10+
logF(info, "Simple message");
11+
EXPECT_THAT(
12+
testing::internal::GetCapturedStderr(),
13+
MatchesRegex(
14+
".+T.+Z \\[info\\] \\[logging_test.cc:10\\] Simple message\n"));
15+
};
16+
17+
TEST(log, specifying_ctx) {
18+
testing::internal::CaptureStderr();
19+
log(info, "MyCtx", "Message");
20+
EXPECT_THAT(testing::internal::GetCapturedStderr(),
21+
MatchesRegex(".+T.+Z \\[info\\] \\[MyCtx\\] Message\n"));
22+
};
23+
24+
TEST(log, formatting_parameters) {
25+
testing::internal::CaptureStderr();
26+
log(info, "MyCtx", "String={} Int={}", "Hello", 42);
27+
EXPECT_THAT(
28+
testing::internal::GetCapturedStderr(),
29+
MatchesRegex(".+T.+Z \\[info\\] \\[MyCtx\\] String=Hello Int=42\n"));
30+
};

0 commit comments

Comments
 (0)