Skip to content

Commit 3eafd7f

Browse files
authored
Merge pull request #12 from rsps/add-output-utils
Add output and logging utils
2 parents e9500c4 + 6ce50da commit 3eafd7f

File tree

20 files changed

+2441
-12
lines changed

20 files changed

+2441
-12
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1414
* `CPM.cmake` script that downloads specified version of [CPM](https://github.com/cpm-cmake/CPM.cmake).
1515
* `fail_in_source_build()`, `extract_value()`, `requires_arguments()` and `safeguard_properties()` utils functions, in `helpers.cmake`.
1616
* `dump()` and `dd()` in `debug.cmake`.
17+
* ANSI utils, in `output.cmake`
1718
* `semver_parse()`, `write_version_file` and `version_from_file()` utils, in `version.cmake`.
1819
* `git_find_version_tag()` util, in `git.cmake`.
1920
* `VERSION` file.
2021
* RSP's GCC strict compile options, in `gcc.cmake` (_exposed via `compiler.cmake` module_).
2122
* Caching utilities, `cache.cmake`.
23+
* `output()` helper, in `output.cmake`.
24+
* Support for ANSI, in `output.cmake`.
25+
* PSR inspired logging functions, in `logging.cmake`.
2226
* A "mini" testing framework for cmake modules and scripts, in `testing.cmake`.
2327
* `RSP_CMAKE_SCRIPTS_BUILD_TESTS` project option for building tests.
2428
* `tests.yaml` and `deploy-docs.yaml` GitHub Actions workflows.

CMakeLists.txt

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
cmake_minimum_required(VERSION 3.30)
66

77
option(RSP_CMAKE_SCRIPTS_BUILD_TESTS "Build tests for the RSP CMake Scripts project" off)
8+
option(RSP_ENABLE_ANSI "Enable ANSI output" false)
89

910
# -------------------------------------------------------------------------------------------------------------- #
1011
# Setup
@@ -65,6 +66,27 @@ if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
6566
include("dev-dependencies.cmake")
6667
endif()
6768

69+
# -------------------------------------------------------------------------------------------------------------- #
70+
# Toggle ANSI output
71+
# -------------------------------------------------------------------------------------------------------------- #
72+
73+
include("rsp/output")
74+
75+
if (RSP_ENABLE_ANSI)
76+
enable_ansi()
77+
else ()
78+
disable_ansi()
79+
endif ()
80+
81+
# -------------------------------------------------------------------------------------------------------------- #
82+
# Post-dependencies project setup
83+
# -------------------------------------------------------------------------------------------------------------- #
84+
85+
if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
86+
include("rsp/debug")
87+
include("rsp/logging")
88+
endif()
89+
6890
# -------------------------------------------------------------------------------------------------------------- #
6991
# Tests
7092
# -------------------------------------------------------------------------------------------------------------- #
@@ -78,6 +100,5 @@ endif ()
78100
# Misc.
79101
# -------------------------------------------------------------------------------------------------------------- #
80102

81-
# include("rsp/debug")
82-
83-
# dump(CMAKE_MODULE_PATH FOO BAR PROJECT_NAME)
103+
# output_ansi_demo()
104+
# dump(CMAKE_MODULE_PATH FOO BAR PROJECT_NAME)

cmake/rsp/logging.cmake

Lines changed: 393 additions & 0 deletions
Large diffs are not rendered by default.

cmake/rsp/logging/utils.cmake

Lines changed: 325 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,325 @@
1+
# -------------------------------------------------------------------------------------------------------------- #
2+
# Logging utilities
3+
# -------------------------------------------------------------------------------------------------------------- #
4+
5+
include_guard(GLOBAL)
6+
7+
# Debug
8+
message(VERBOSE "rsp/logging/utils module included")
9+
10+
include("rsp/output")
11+
12+
# -------------------------------------------------------------------------------------------------------------- #
13+
# Defaults
14+
# -------------------------------------------------------------------------------------------------------------- #
15+
16+
if (NOT DEFINED RSP_LOG_LEVELS)
17+
18+
# System is unusable.
19+
set(EMERGENCY_LEVEL "emergency" CACHE STRING "RSP log level")
20+
21+
# Action must be taken immediately.
22+
set(ALERT_LEVEL "alert" CACHE STRING "RSP log level")
23+
24+
# Critical conditions.
25+
set(CRITICAL_LEVEL "critical" CACHE STRING "RSP log level")
26+
27+
# Runtime errors that do not require immediate action but should typically
28+
# be logged and monitored.
29+
set(ERROR_LEVEL "error" CACHE STRING "RSP log level")
30+
31+
# Exceptional occurrences that are not errors.
32+
set(WARNING_LEVEL "warning" CACHE STRING "RSP log level")
33+
34+
# Normal but significant events.
35+
set(NOTICE_LEVEL "notice" CACHE STRING "RSP log level")
36+
37+
# Interesting events.
38+
set(INFO_LEVEL "info" CACHE STRING "RSP log level")
39+
40+
# Detailed debug information.
41+
set(DEBUG_LEVEL "debug" CACHE STRING "RSP log level")
42+
43+
# PSR Log levels
44+
#
45+
# @see https://www.php-fig.org/psr/psr-3/#5-psrlogloglevel
46+
#
47+
set(RSP_LOG_LEVELS
48+
"${EMERGENCY_LEVEL}"
49+
"${ALERT_LEVEL}"
50+
"${CRITICAL_LEVEL}"
51+
"${ERROR_LEVEL}"
52+
"${WARNING_LEVEL}"
53+
"${NOTICE_LEVEL}"
54+
"${INFO_LEVEL}"
55+
"${DEBUG_LEVEL}"
56+
57+
CACHE STRING "RSP log levels"
58+
)
59+
endif ()
60+
61+
if (NOT DEFINED RSP_LOG_LEVELS_CMAKE)
62+
63+
# A "map" of the PSR defined log levels and what each
64+
# level corresponds to for cmake's modes / types.
65+
#
66+
# This "map" is used internally by log(), when no `mode`
67+
# argument has been specified.
68+
#
69+
# @see https://www.php-fig.org/psr/psr-3/#5-psrlogloglevel
70+
# @see https://cmake.org/cmake/help/latest/command/message.html#general-messages
71+
#
72+
set(RSP_LOG_LEVELS_CMAKE
73+
"${EMERGENCY_LEVEL} FATAL_ERROR"
74+
"${ALERT_LEVEL} FATAL_ERROR"
75+
"${CRITICAL_LEVEL} FATAL_ERROR"
76+
"${ERROR_LEVEL} SEND_ERROR"
77+
"${WARNING_LEVEL} WARNING"
78+
"${NOTICE_LEVEL} NOTICE"
79+
"${INFO_LEVEL} NOTICE"
80+
"${DEBUG_LEVEL} DEBUG"
81+
82+
CACHE STRING " RSP log levels map for cmake's message mode"
83+
)
84+
endif ()
85+
86+
if (NOT DEFINED RSP_LOG_SHOW_TIMESTAMP)
87+
88+
# Show timestamp for log entries
89+
set(RSP_LOG_SHOW_TIMESTAMP true CACHE BOOL " RSP show timestamp for log entries")
90+
endif ()
91+
92+
if (NOT DEFINED RSP_LOG_TIMESTAMP_FORMAT)
93+
94+
# Log timestamp format
95+
set(RSP_LOG_TIMESTAMP_FORMAT "%Y-%m-%d %H:%M:%S.%f" CACHE STRING " RSP log timestamp format")
96+
endif ()
97+
98+
if (NOT DEFINED RSP_LOG_TIMESTAMP_UTC)
99+
100+
# Use UTC or local time...
101+
set(RSP_LOG_TIMESTAMP_UTC false CACHE BOOL " RSP log timestamp UTC state (true if UTC, false if local)")
102+
endif ()
103+
104+
if (NOT DEFINED RSP_LOG_INDENT)
105+
106+
# Indentation for log context, timestamp,...etc
107+
set(RSP_LOG_INDENT " " CACHE STRING " RSP ident for log context, timestamp...etc")
108+
endif ()
109+
110+
# -------------------------------------------------------------------------------------------------------------- #
111+
# Functions & Macros
112+
# -------------------------------------------------------------------------------------------------------------- #
113+
114+
if (NOT COMMAND "resolve_cmake_message_mode")
115+
116+
#! resolve_cmake_message_mode : Resolves the CMake message mode acc. to requested
117+
# PSR log level.
118+
#
119+
# WARNING: Macro is intended to be used internally within misc. log
120+
# functions.
121+
#
122+
# @see https://cmake.org/cmake/help/latest/command/message.html#general-messages
123+
# @see rsp/output/utils::resolve_msg_mode
124+
#
125+
# @internal
126+
#
127+
macro(resolve_cmake_message_mode)
128+
# Resolve CMake's message mode, acc. to specified log level.
129+
set(default_cmake_msg_mode "NOTICE")
130+
131+
foreach (lvl IN LISTS RSP_LOG_LEVELS_CMAKE)
132+
string(REPLACE " " ";" parts "${lvl}")
133+
134+
# psr = PSR log level, value = CMake message mode
135+
list(GET parts 0 psr)
136+
list(GET parts 1 msg_mode_cmake)
137+
138+
if ("${psr}" STREQUAL "${log_level}")
139+
message(VERBOSE "Mapping ${log_level} level to cmake message mode ${msg_mode_cmake}")
140+
141+
set(default_cmake_msg_mode "${msg_mode_cmake}")
142+
unset(psr)
143+
unset(msg_mode_cmake)
144+
145+
break()
146+
endif ()
147+
endforeach ()
148+
149+
# - Resolve the actual `msg_mode`. If none is given, then `default_cmake_msg_mode` is used!
150+
resolve_msg_mode("${default_cmake_msg_mode}")
151+
endmacro()
152+
endif ()
153+
154+
if (NOT COMMAND "format_log_level_label")
155+
156+
#! format_log_level_label : Formats a label, acc. to specified log level
157+
#
158+
# @param <string> level Log level
159+
# @param <variable> output The output variable to assign result to
160+
#
161+
# @return
162+
# output The formatted label
163+
#
164+
function(format_log_level_label level output)
165+
set(style_affix "_style")
166+
167+
set("${EMERGENCY_LEVEL}${style_affix}" "${COLOR_BRIGHT_RED}${TEXT_BOLD}")
168+
set("${ALERT_LEVEL}${style_affix}" "${COLOR_BRIGHT_RED}${TEXT_BOLD}")
169+
set("${CRITICAL_LEVEL}${style_affix}" "${COLOR_RED}${TEXT_BOLD}")
170+
set("${ERROR_LEVEL}${style_affix}" "${COLOR_RED}${TEXT_BOLD}")
171+
set("${WARNING_LEVEL}${style_affix}" "${COLOR_YELLOW}${TEXT_BOLD}")
172+
set("${NOTICE_LEVEL}${style_affix}" "${COLOR_BRIGHT_BLUE}")
173+
set("${INFO_LEVEL}${style_affix}" "${COLOR_BLUE}")
174+
set("${DEBUG_LEVEL}${style_affix}" "${COLOR_WHITE}")
175+
176+
# Abort if no style can be found for requested level
177+
if (NOT DEFINED "${level}${style_affix}")
178+
message(FATAL_ERROR "Unable to find style for log level: ${level}")
179+
endif ()
180+
181+
# Apply evt. additional formatting of label...
182+
set(label "${level}")
183+
184+
set("${output}" "${${level}${style_affix}}${label}${RESTORE}")
185+
186+
return(PROPAGATE "${output}")
187+
endfunction()
188+
endif ()
189+
190+
if (NOT COMMAND "format_log_message")
191+
192+
#! format_log_message : Formats given log message, acc. to given log level
193+
#
194+
# @param <string> level Log level
195+
# @param <string> message The log message
196+
# @param <variable> output The output variable to assign result to
197+
#
198+
# @return
199+
# output The formatted message
200+
#
201+
function(format_log_message level message output)
202+
set(style_affix "_style")
203+
204+
set("${EMERGENCY_LEVEL}${style_affix}" "${TEXT_BOLD}${TEXT_ITALIC}")
205+
set("${ALERT_LEVEL}${style_affix}" "${TEXT_BOLD}")
206+
set("${CRITICAL_LEVEL}${style_affix}" "${TEXT_BOLD}")
207+
set("${ERROR_LEVEL}${style_affix}" "")
208+
set("${WARNING_LEVEL}${style_affix}" "${TEXT_ITALIC}")
209+
set("${NOTICE_LEVEL}${style_affix}" "")
210+
set("${INFO_LEVEL}${style_affix}" "")
211+
set("${DEBUG_LEVEL}${style_affix}" "${TEXT_ITALIC}")
212+
213+
# Abort if no style can be found for requested level
214+
if (NOT DEFINED "${level}${style_affix}")
215+
message(FATAL_ERROR "Unable to style message for log level: ${level}")
216+
endif ()
217+
218+
# Apply evt. additional formatting of message...
219+
set(formatted "${message}")
220+
221+
set("${output}" "${${level}${style_affix}}${formatted}${RESTORE}")
222+
223+
return(PROPAGATE "${output}")
224+
endfunction()
225+
endif ()
226+
227+
if (NOT COMMAND "format_log_context")
228+
229+
#! format_log_context : Formats evt. context variables, acc. to log level
230+
#
231+
# @param <string> level Log level
232+
# @param <list> context List of variables
233+
# @param <variable> output The output variable to assign result to
234+
#
235+
# @return
236+
# output The formatted log context
237+
#
238+
function(format_log_context level context output)
239+
set(style_affix "_style")
240+
241+
set("${EMERGENCY_LEVEL}${style_affix}" "")
242+
set("${ALERT_LEVEL}${style_affix}" "")
243+
set("${CRITICAL_LEVEL}${style_affix}" "")
244+
set("${ERROR_LEVEL}${style_affix}" "")
245+
set("${WARNING_LEVEL}${style_affix}" "")
246+
set("${NOTICE_LEVEL}${style_affix}" "")
247+
set("${INFO_LEVEL}${style_affix}" "")
248+
set("${DEBUG_LEVEL}${style_affix}" "")
249+
250+
# Abort if no style can be found for requested level
251+
if (NOT DEFINED "${level}${style_affix}")
252+
message(FATAL_ERROR "Unable to style context for log level: ${level}")
253+
endif ()
254+
255+
# ---------------------------------------------------------------------------------------------- #
256+
257+
set(indent "${RSP_LOG_INDENT}")
258+
set(buffer "")
259+
260+
# ---------------------------------------------------------------------------------------------- #
261+
262+
string(APPEND buffer "\n${indent}[")
263+
264+
foreach (prop IN LISTS context)
265+
266+
set(entry "${COLOR_BRIGHT_MAGENTA}${TEXT_ITALIC}${prop}${RESTORE} = ${${prop}}")
267+
268+
string(REPEAT "${indent}" 2 i)
269+
string(APPEND buffer "\n${i}${entry}")
270+
endforeach ()
271+
272+
string(APPEND buffer "\n${indent}]")
273+
274+
set(formatted "${COLOR_MAGENTA}Context${RESTORE}: ${buffer}")
275+
276+
# ---------------------------------------------------------------------------------------------- #
277+
278+
set("${output}" "\n${indent}${${level}${style_affix}}${formatted}${RESTORE}")
279+
280+
return(PROPAGATE "${output}")
281+
endfunction()
282+
endif ()
283+
284+
if (NOT COMMAND "format_log_timestamp")
285+
286+
#! format_log_timestamp : Formats current timestamp, acc. to given log level
287+
#
288+
# @param <string> level Log level
289+
# @param <variable> output The output variable to assign result to
290+
#
291+
# @return
292+
# output The formatted timestamp
293+
#
294+
function(format_log_timestamp level output)
295+
set(style_affix "_style")
296+
297+
set("${EMERGENCY_LEVEL}${style_affix}" "")
298+
set("${ALERT_LEVEL}${style_affix}" "")
299+
set("${CRITICAL_LEVEL}${style_affix}" "")
300+
set("${ERROR_LEVEL}${style_affix}" "")
301+
set("${WARNING_LEVEL}${style_affix}" "")
302+
set("${NOTICE_LEVEL}${style_affix}" "")
303+
set("${INFO_LEVEL}${style_affix}" "")
304+
set("${DEBUG_LEVEL}${style_affix}" "")
305+
306+
# Abort if no style can be found for requested level
307+
if (NOT DEFINED "${level}${style_affix}")
308+
message(FATAL_ERROR "Unable to style timestamp for log level: ${level}")
309+
endif ()
310+
311+
# Obtain current timestamp
312+
if (RSP_LOG_TIMESTAMP_UTC)
313+
string(TIMESTAMP now "${RSP_LOG_TIMESTAMP_FORMAT}" UTC)
314+
else()
315+
string(TIMESTAMP now "${RSP_LOG_TIMESTAMP_FORMAT}")
316+
endif ()
317+
318+
# Format output...
319+
set(formatted "${COLOR_MAGENTA}${TEXT_ITALIC}Timestamp${RESTORE}: ${now}")
320+
321+
set("${output}" "\n${RSP_LOG_INDENT}${${level}${style_affix}}${formatted}${RESTORE}")
322+
323+
return(PROPAGATE "${output}")
324+
endfunction()
325+
endif ()

0 commit comments

Comments
 (0)