Skip to content

Commit d36cb80

Browse files
authored
Merge pull request #54 from rm5248/LOGCXX-515
[Logcxx-515] Add fmt support
2 parents 96ef0cc + 5b7b835 commit d36cb80

File tree

4 files changed

+192
-0
lines changed

4 files changed

+192
-0
lines changed

src/examples/cpp/CMakeLists.txt

+9
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,12 @@ endforeach()
1010
configure_file( custom-appender.xml
1111
${CMAKE_CURRENT_BINARY_DIR}/custom-appender.xml
1212
COPYONLY )
13+
14+
# Custom handling for format string example, since it utilizes libfmt
15+
find_package(fmt QUIET)
16+
if(${fmt_FOUND})
17+
add_executable( format-string format-string.cpp )
18+
target_compile_definitions(format-string PRIVATE ${LOG4CXX_COMPILE_DEFINITIONS} ${APR_COMPILE_DEFINITIONS} ${APR_UTIL_COMPILE_DEFINITIONS} )
19+
target_include_directories(format-string PRIVATE ${CMAKE_CURRENT_LIST_DIR} $<TARGET_PROPERTY:log4cxx,INCLUDE_DIRECTORIES>)
20+
target_link_libraries(format-string PRIVATE log4cxx ${APR_UTIL_LIBRARIES} ${XMLLIB_LIBRARIES} ${APR_LIBRARIES} ${APR_SYSTEM_LIBS} fmt::fmt)
21+
endif(${fmt_FOUND})

src/examples/cpp/format-string.cpp

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
#include <stdlib.h>
19+
#include <log4cxx/basicconfigurator.h>
20+
#include <locale.h>
21+
#include <fmt/core.h>
22+
#include <fmt/color.h>
23+
#include <fmt/ostream.h>
24+
#include <iomanip>
25+
26+
using namespace log4cxx;
27+
using namespace log4cxx::helpers;
28+
29+
struct MyStruct {
30+
int x;
31+
};
32+
33+
std::ostream& operator<<( std::ostream& stream, const MyStruct& mystruct ){
34+
stream << "[MyStruct x:" << mystruct.x << "]";
35+
return stream;
36+
}
37+
38+
int main()
39+
{
40+
setlocale(LC_ALL, "");
41+
42+
BasicConfigurator::configure();
43+
LoggerPtr rootLogger = Logger::getRootLogger();
44+
45+
LOG4CXX_INFO_FMT( rootLogger, "This is a {} mesage", "test" );
46+
LOG4CXX_INFO_FMT( rootLogger, fmt::fg(fmt::color::red), "Messages can be colored" );
47+
LOG4CXX_INFO_FMT( rootLogger, "We can also align text to the {:<10} or {:>10}", "left", "right" );
48+
49+
MyStruct mine;
50+
LOG4CXX_INFO_FMT( rootLogger, "This custom type {} can also be logged, since it implements operator<<", mine );
51+
52+
LOG4CXX_INFO( rootLogger, "Numbers can be formatted with excessive operator<<: "
53+
<< std::setprecision(3) << 22.456
54+
<< " And as hex: "
55+
<< std::setbase( 16 ) << 123 );
56+
LOG4CXX_INFO_FMT( rootLogger, "Numbers can be formatted with a format string {:.1f} and as hex: {:x}", 22.456, 123 );
57+
58+
return 0;
59+
}

src/main/include/log4cxx/logger.h

+90
Original file line numberDiff line numberDiff line change
@@ -1770,6 +1770,17 @@ Logs a message to a specified logger with a specified level.
17701770
::log4cxx::helpers::MessageBuffer oss_; \
17711771
logger->forcedLog(level, oss_.str(oss_ << message), LOG4CXX_LOCATION); }} while (0)
17721772

1773+
/**
1774+
Logs a message to a specified logger with a specified level, formatting utilizing libfmt
1775+
1776+
@param logger the logger to be used.
1777+
@param level the level to log.
1778+
@param ... The format string and message to log
1779+
*/
1780+
#define LOG4CXX_LOG_FMT(logger, level, ...) do { \
1781+
if (logger->isEnabledFor(level)) {\
1782+
logger->forcedLog(level, fmt::format( __VA_ARGS__ ), LOG4CXX_LOCATION); }} while (0)
1783+
17731784
/**
17741785
Logs a message to a specified logger with a specified level.
17751786
@@ -1793,8 +1804,19 @@ Logs a message to a specified logger with the DEBUG level.
17931804
if (LOG4CXX_UNLIKELY(logger->isDebugEnabled())) {\
17941805
::log4cxx::helpers::MessageBuffer oss_; \
17951806
logger->forcedLog(::log4cxx::Level::getDebug(), oss_.str(oss_ << message), LOG4CXX_LOCATION); }} while (0)
1807+
1808+
/**
1809+
Logs a message to a specified logger with the DEBUG level, formatting with libfmt
1810+
1811+
@param logger the logger to be used.
1812+
@param ... The format string and message to log
1813+
*/
1814+
#define LOG4CXX_DEBUG_FMT(logger, ...) do { \
1815+
if (LOG4CXX_UNLIKELY(logger->isDebugEnabled())) {\
1816+
logger->forcedLog(::log4cxx::Level::getDebug(), fmt::format( __VA_ARGS__ ), LOG4CXX_LOCATION); }} while (0)
17961817
#else
17971818
#define LOG4CXX_DEBUG(logger, message)
1819+
#define LOG4CXX_DEBUG_FMT(logger, ...)
17981820
#endif
17991821

18001822
#if !defined(LOG4CXX_THRESHOLD) || LOG4CXX_THRESHOLD <= 5000
@@ -1808,8 +1830,19 @@ Logs a message to a specified logger with the TRACE level.
18081830
if (LOG4CXX_UNLIKELY(logger->isTraceEnabled())) {\
18091831
::log4cxx::helpers::MessageBuffer oss_; \
18101832
logger->forcedLog(::log4cxx::Level::getTrace(), oss_.str(oss_ << message), LOG4CXX_LOCATION); }} while (0)
1833+
1834+
/**
1835+
Logs a message to a specified logger with the TRACE level, formatting with libfmt.
1836+
1837+
@param logger the logger to be used.
1838+
@param ... The format string and message to log
1839+
*/
1840+
#define LOG4CXX_TRACE_FMT(logger, ...) do { \
1841+
if (LOG4CXX_UNLIKELY(logger->isTraceEnabled())) {\
1842+
logger->forcedLog(::log4cxx::Level::getTrace(), fmt::format( __VA_ARGS__ ), LOG4CXX_LOCATION); }} while (0)
18111843
#else
18121844
#define LOG4CXX_TRACE(logger, message)
1845+
#define LOG4CXX_TRACE_FMT(logger, ...)
18131846
#endif
18141847

18151848
#if !defined(LOG4CXX_THRESHOLD) || LOG4CXX_THRESHOLD <= 20000
@@ -1823,8 +1856,20 @@ Logs a message to a specified logger with the INFO level.
18231856
if (logger->isInfoEnabled()) {\
18241857
::log4cxx::helpers::MessageBuffer oss_; \
18251858
logger->forcedLog(::log4cxx::Level::getInfo(), oss_.str(oss_ << message), LOG4CXX_LOCATION); }} while (0)
1859+
1860+
/**
1861+
Logs a message to a specified logger with the INFO level, formatting with libfmt.
1862+
1863+
@param logger the logger to be used.
1864+
@param message the message string to log.
1865+
@param ... The format string and message to log
1866+
*/
1867+
#define LOG4CXX_INFO_FMT(logger, ...) do { \
1868+
if (logger->isInfoEnabled()) {\
1869+
logger->forcedLog(::log4cxx::Level::getInfo(), fmt::format( __VA_ARGS__ ), LOG4CXX_LOCATION); }} while (0)
18261870
#else
18271871
#define LOG4CXX_INFO(logger, message)
1872+
#define LOG4CXX_INFO_FMT(logger, ...)
18281873
#endif
18291874

18301875
#if !defined(LOG4CXX_THRESHOLD) || LOG4CXX_THRESHOLD <= 30000
@@ -1838,8 +1883,19 @@ Logs a message to a specified logger with the WARN level.
18381883
if (logger->isWarnEnabled()) {\
18391884
::log4cxx::helpers::MessageBuffer oss_; \
18401885
logger->forcedLog(::log4cxx::Level::getWarn(), oss_.str(oss_ << message), LOG4CXX_LOCATION); }} while (0)
1886+
1887+
/**
1888+
Logs a message to a specified logger with the WARN level, formatting with libfmt
1889+
1890+
@param logger the logger to be used.
1891+
@param ... The format string and message to log
1892+
*/
1893+
#define LOG4CXX_WARN_FMT(logger, ...) do { \
1894+
if (logger->isWarnEnabled()) {\
1895+
logger->forcedLog(::log4cxx::Level::getWarn(), fmt::format( __VA_ARGS__ ), LOG4CXX_LOCATION); }} while (0)
18411896
#else
18421897
#define LOG4CXX_WARN(logger, message)
1898+
#define LOG4CXX_WARN_FMT(logger, ...)
18431899
#endif
18441900

18451901
#if !defined(LOG4CXX_THRESHOLD) || LOG4CXX_THRESHOLD <= 40000
@@ -1854,6 +1910,16 @@ Logs a message to a specified logger with the ERROR level.
18541910
::log4cxx::helpers::MessageBuffer oss_; \
18551911
logger->forcedLog(::log4cxx::Level::getError(), oss_.str(oss_ << message), LOG4CXX_LOCATION); }} while (0)
18561912

1913+
/**
1914+
Logs a message to a specified logger with the ERROR level, formatting with libfmt
1915+
1916+
@param logger the logger to be used.
1917+
@param ... The format string and message to log
1918+
*/
1919+
#define LOG4CXX_ERROR_FMT(logger, ...) do { \
1920+
if (logger->isErrorEnabled()) {\
1921+
logger->forcedLog(::log4cxx::Level::getError(), fmt::format( __VA_ARGS__ ), LOG4CXX_LOCATION); }} while (0)
1922+
18571923
/**
18581924
Logs a error if the condition is not true.
18591925
@@ -1866,9 +1932,22 @@ Logs a error if the condition is not true.
18661932
::log4cxx::helpers::MessageBuffer oss_; \
18671933
logger->forcedLog(::log4cxx::Level::getError(), oss_.str(oss_ << message), LOG4CXX_LOCATION); }} while (0)
18681934

1935+
/**
1936+
Logs a error if the condition is not true, formatting with libfmt
1937+
1938+
@param logger the logger to be used.
1939+
@param condition condition
1940+
@param ... The format string and message to log
1941+
*/
1942+
#define LOG4CXX_ASSERT_FMT(logger, condition, ...) do { \
1943+
if (!(condition) && logger->isErrorEnabled()) {\
1944+
logger->forcedLog(::log4cxx::Level::getError(), fmt::format( __VA_ARGS__ ), LOG4CXX_LOCATION); }} while (0)
1945+
18691946
#else
18701947
#define LOG4CXX_ERROR(logger, message)
1948+
#define LOG4CXX_ERROR_FMT(logger, ...)
18711949
#define LOG4CXX_ASSERT(logger, condition, message)
1950+
#define LOG4CXX_ASSERT_FMT(logger, condition, ...)
18721951
#endif
18731952

18741953
#if !defined(LOG4CXX_THRESHOLD) || LOG4CXX_THRESHOLD <= 50000
@@ -1882,8 +1961,19 @@ Logs a message to a specified logger with the FATAL level.
18821961
if (logger->isFatalEnabled()) {\
18831962
::log4cxx::helpers::MessageBuffer oss_; \
18841963
logger->forcedLog(::log4cxx::Level::getFatal(), oss_.str(oss_ << message), LOG4CXX_LOCATION); }} while (0)
1964+
1965+
/**
1966+
Logs a message to a specified logger with the FATAL level, formatting with libfmt
1967+
1968+
@param logger the logger to be used.
1969+
@param ... The format string and message to log
1970+
*/
1971+
#define LOG4CXX_FATAL_FMT(logger, ...) do { \
1972+
if (logger->isFatalEnabled()) {\
1973+
logger->forcedLog(::log4cxx::Level::getFatal(), fmt::format( __VA_ARGS__ ), LOG4CXX_LOCATION); }} while (0)
18851974
#else
18861975
#define LOG4CXX_FATAL(logger, message)
1976+
#define LOG4CXX_FATAL_FMT(logger, ...)
18871977
#endif
18881978

18891979
/**

src/site/markdown/usage.md

+34
Original file line numberDiff line numberDiff line change
@@ -797,6 +797,40 @@ This will output data similar to the following:
797797
0 [0x7fd1eed63bc0] INFO root null - Some important information: [MyStruct x:90]
798798
~~~
799799

800+
# Logging with {fmt} {#logging-with-fmt}
801+
802+
One issue with utilizing log4cxx and its ostream style of logging is that log
803+
statements can be very awkward if you need to precisely format something:
804+
805+
~~~{.cpp}
806+
LOG4CXX_INFO( rootLogger, "Numbers can be formatted with excessive operator<<: "
807+
<< std::setprecision(3) << 22.456
808+
<< " And as hex: "
809+
<< std::setbase( 16 ) << 123 );
810+
~~~
811+
812+
This leads to very awkward code to read and write, especially as iostreams don't
813+
support positional arguments at all.
814+
815+
In order to get around this, one popular library(that has been standardized as
816+
part of C++20) is [{fmt}](https://fmt.dev/latest/index.html). Supporting
817+
positional arguments and printf-like formatting, it makes for much clearer
818+
code like the following:
819+
820+
~~~{.cpp}
821+
LOG4CXX_INFO_FMT( rootLogger, "Numbers can be formatted with a format string {:.1f} and as hex: {:x}", 22.456, 123 );
822+
~~~
823+
824+
Note that log4cxx does not include a copy of {fmt}, so you must include the
825+
correct headers and linker flags in order to use the `LOG4CXX_[level]_FMT`
826+
family of macros.
827+
828+
As with the standard logger macros, these macros will also be compiled out
829+
if the `LOG4CXX_THRESHOLD` macro is set to a level that will compile out
830+
the non-FMT macros.
831+
832+
A full example can be seen in the src/examples/cpp/format-string.cpp file.
833+
800834
# Conclusions {#conclusions}
801835

802836
Apache Log4cxx is a popular logging package written in C++. One of its

0 commit comments

Comments
 (0)