/src/dcmtk/oflog/libsrc/oflog.cc
Line | Count | Source |
1 | | /* |
2 | | * |
3 | | * Copyright (C) 2009-2026, OFFIS e.V. |
4 | | * All rights reserved. See COPYRIGHT file for details. |
5 | | * |
6 | | * This software and supporting documentation were developed by |
7 | | * |
8 | | * OFFIS e.V. |
9 | | * R&D Division Health |
10 | | * Escherweg 2 |
11 | | * D-26121 Oldenburg, Germany |
12 | | * |
13 | | * |
14 | | * Module: oflog |
15 | | * |
16 | | * Author: Uli Schlachter |
17 | | * |
18 | | * Purpose: Simplify the usage of log4cplus to other modules |
19 | | * |
20 | | */ |
21 | | |
22 | | #include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ |
23 | | #include "dcmtk/oflog/oflog.h" |
24 | | |
25 | | #include "dcmtk/ofstd/ofstd.h" |
26 | | #include "dcmtk/ofstd/ofdate.h" |
27 | | #include "dcmtk/ofstd/oftime.h" |
28 | | #include "dcmtk/ofstd/ofconapp.h" |
29 | | |
30 | | #include "dcmtk/oflog/configrt.h" |
31 | | #include "dcmtk/oflog/consap.h" |
32 | | #include "dcmtk/oflog/helpers/loglog.h" |
33 | | #include "dcmtk/oflog/helpers/socket.h" |
34 | | #include "dcmtk/oflog/helpers/strhelp.h" |
35 | | #include "dcmtk/oflog/internal/internal.h" |
36 | | |
37 | | OFunique_ptr<dcmtk::log4cplus::helpers::Properties> OFLog::configProperties_; |
38 | | |
39 | | OFLogger::OFLogger(const dcmtk::log4cplus::Logger &base) |
40 | 14 | : dcmtk::log4cplus::Logger(base) |
41 | 14 | { |
42 | 14 | } |
43 | | |
44 | | static void OFLog_init() |
45 | 20 | { |
46 | | // we don't have to protect against threads here, this function is guaranteed |
47 | | // to be called at least once before main() starts -> no threads yet |
48 | 20 | static int initialized = 0; |
49 | 20 | if (initialized) |
50 | 14 | return; |
51 | 6 | initialized = 1; |
52 | | |
53 | | #ifdef HAVE_WINSOCK_H |
54 | | // initialize Winsock DLL in order to use gethostname() and the like |
55 | | WSAData winSockData; |
56 | | // we need at least version 1.1 |
57 | | WORD winSockVersionNeeded = MAKEWORD(1, 1); |
58 | | WSAStartup(winSockVersionNeeded, &winSockData); |
59 | | #endif |
60 | | |
61 | | // we default to a really simple pattern: loglevel_prefix: message\n |
62 | 6 | const char *pattern = "%P: %m%n"; |
63 | 6 | OFunique_ptr<dcmtk::log4cplus::Layout> layout(new dcmtk::log4cplus::PatternLayout(pattern)); |
64 | 6 | dcmtk::log4cplus::SharedAppenderPtr console(new dcmtk::log4cplus::ConsoleAppender(OFTrue /* logToStdErr */, OFTrue /* immediateFlush */)); |
65 | 6 | dcmtk::log4cplus::Logger rootLogger = dcmtk::log4cplus::Logger::getRoot(); |
66 | | |
67 | 6 | console->setLayout(OFmove(layout)); |
68 | 6 | rootLogger.addAppender(console); |
69 | 6 | rootLogger.setLogLevel(dcmtk::log4cplus::INFO_LOG_LEVEL); |
70 | 6 | } |
71 | | |
72 | | // private class, this class's constructor makes sure that OFLog_init() is |
73 | | // called at least once before main() runs |
74 | | class static_OFLog_initializer |
75 | | { |
76 | | public: |
77 | | static_OFLog_initializer() |
78 | 6 | { |
79 | 6 | OFLog_init(); |
80 | 6 | } |
81 | | } static initializer; |
82 | | |
83 | | void OFLog::configureLogger(dcmtk::log4cplus::LogLevel level) |
84 | 0 | { |
85 | | // This assumes that OFLog_init() was already called. We keep using its |
86 | | // setup and just change the log level. |
87 | 0 | dcmtk::log4cplus::Logger rootLogger = dcmtk::log4cplus::Logger::getRoot(); |
88 | 0 | rootLogger.setLogLevel(level); |
89 | 0 | } |
90 | | |
91 | | OFLogger OFLog::getLogger(const char *loggerName) |
92 | 14 | { |
93 | 14 | OFLog_init(); |
94 | | // logger objects have a reference counting copy-constructor, so returning by-value is cheap |
95 | 14 | return dcmtk::log4cplus::Logger::getInstance(loggerName); |
96 | 14 | } |
97 | | |
98 | | OFLogger OFLog::getLogger(const OFString &loggerName) |
99 | 0 | { |
100 | | // use existing C string version |
101 | 0 | return getLogger(loggerName.c_str()); |
102 | 0 | } |
103 | | |
104 | | /** Adds our known variables to the Properties instance |
105 | | * @param props instance to add the variables to |
106 | | * @param cmd command line which we use for getting the program name, |
107 | | * may be NULL |
108 | | */ |
109 | | static void addVariables(dcmtk::log4cplus::helpers::Properties &props, OFCommandLine* cmd) |
110 | 0 | { |
111 | 0 | OFString date; |
112 | 0 | OFString time; |
113 | | |
114 | | // Set ${appname}, if possible |
115 | 0 | if (cmd) { |
116 | 0 | OFString app; |
117 | 0 | OFStandard::getFilenameFromPath(app, cmd->getProgramName()); |
118 | 0 | props.setProperty("appname", app); |
119 | 0 | } |
120 | |
|
121 | 0 | OFDate::getCurrentDate().getISOFormattedDate(date, OFFalse); |
122 | 0 | OFTime::getCurrentTime().getISOFormattedTime(time, OFTrue, OFFalse, OFFalse, OFFalse); |
123 | | |
124 | | // Set some other useful variables |
125 | 0 | props.setProperty("hostname", dcmtk::log4cplus::helpers::getHostname(OFFalse)); |
126 | 0 | props.setProperty("pid", dcmtk::log4cplus::helpers::convertIntegerToString(OFStandard::getProcessID())); |
127 | 0 | props.setProperty("date", date); |
128 | 0 | props.setProperty("time", time); |
129 | 0 | } |
130 | | |
131 | | void OFLog::reconfigure(OFCommandLine *cmd) |
132 | 0 | { |
133 | 0 | dcmtk::log4cplus::helpers::Properties *props = configProperties_.get(); |
134 | | |
135 | | // If configProperties_ is a NULL pointer, --log-config was never used and |
136 | | // there is nothing we could parse again. |
137 | 0 | if (!props) |
138 | 0 | return; |
139 | | |
140 | | // Add some useful variables to the properties, this really just pretends |
141 | | // the user wrote "variable = value" in the config. |
142 | 0 | addVariables(*props, cmd); |
143 | |
|
144 | 0 | unsigned int flags = 0; |
145 | | // Recursively expand ${vars} |
146 | 0 | flags |= dcmtk::log4cplus::PropertyConfigurator::fRecursiveExpansion; |
147 | | // Try to look up ${vars} internally before asking the environment |
148 | 0 | flags |= dcmtk::log4cplus::PropertyConfigurator::fShadowEnvironment; |
149 | | |
150 | | // Configure log4cplus based on our settings |
151 | 0 | dcmtk::log4cplus::PropertyConfigurator conf(*props, dcmtk::log4cplus::Logger::getDefaultHierarchy(), flags); |
152 | 0 | conf.configure(); |
153 | 0 | } |
154 | | |
155 | | void OFLog::configure(OFLogger::LogLevel level) |
156 | 0 | { |
157 | 0 | configureLogger(level); |
158 | 0 | } |
159 | | |
160 | | void OFLog::configureFromCommandLine(OFCommandLine &cmd, |
161 | | OFConsoleApplication &app, |
162 | | OFLogger::LogLevel defaultLevel) |
163 | 0 | { |
164 | 0 | OFString logLevel = ""; |
165 | 0 | OFString logConfig = ""; |
166 | 0 | dcmtk::log4cplus::LogLevel level = dcmtk::log4cplus::NOT_SET_LOG_LEVEL; |
167 | |
|
168 | 0 | cmd.beginOptionBlock(); |
169 | 0 | if (cmd.findOption("--debug")) |
170 | 0 | level = OFLogger::DEBUG_LOG_LEVEL; |
171 | 0 | if (cmd.findOption("--verbose")) |
172 | 0 | level = OFLogger::INFO_LOG_LEVEL; |
173 | 0 | if (cmd.findOption("--quiet")) |
174 | 0 | level = OFLogger::FATAL_LOG_LEVEL; |
175 | 0 | cmd.endOptionBlock(); |
176 | |
|
177 | 0 | if (cmd.findOption("--log-level")) |
178 | 0 | { |
179 | 0 | app.checkConflict("--log-level", "--verbose, --debug or --quiet", level != dcmtk::log4cplus::NOT_SET_LOG_LEVEL); |
180 | |
|
181 | 0 | app.checkValue(cmd.getValue(logLevel)); |
182 | 0 | level = dcmtk::log4cplus::getLogLevelManager().fromString(logLevel); |
183 | 0 | if (level == dcmtk::log4cplus::NOT_SET_LOG_LEVEL) |
184 | 0 | app.printError("Invalid log level for --log-level option"); |
185 | 0 | } |
186 | |
|
187 | 0 | if (cmd.findOption("--log-config")) |
188 | 0 | { |
189 | 0 | app.checkConflict("--log-config", "--log-level", !logLevel.empty()); |
190 | 0 | app.checkConflict("--log-config", "--verbose, --debug or --quiet", level != dcmtk::log4cplus::NOT_SET_LOG_LEVEL); |
191 | |
|
192 | 0 | app.checkValue(cmd.getValue(logConfig)); |
193 | | |
194 | | // check whether config file exists at all and is readable |
195 | 0 | if (!OFStandard::fileExists(logConfig)) |
196 | 0 | app.printError("Specified --log-config file does not exist"); |
197 | 0 | if (!OFStandard::isReadable(logConfig)) |
198 | 0 | app.printError("Specified --log-config file cannot be read"); |
199 | | |
200 | | // There seems to be no way to get an error value here :( |
201 | | //dcmtk::log4cplus::PropertyConfigurator::doConfigure(logConfig); |
202 | | |
203 | | // This does the same stuff that line above would have done, but it also |
204 | | // does some sanity checks on the config file. |
205 | 0 | configProperties_.reset(new dcmtk::log4cplus::helpers::Properties(logConfig)); |
206 | 0 | if (configProperties_->size() == 0) |
207 | 0 | app.printError("Specified --log-config file does not contain any settings"); |
208 | 0 | if (configProperties_->getPropertySubset("log4cplus.").size() == 0) |
209 | 0 | app.printError("Specified --log-config file does not contain any valid settings"); |
210 | 0 | if (!configProperties_->exists("log4cplus.rootLogger")) |
211 | 0 | app.printError("Specified --log-config file does not set up log4cplus.rootLogger"); |
212 | |
|
213 | 0 | reconfigure(&cmd); |
214 | 0 | } |
215 | 0 | else |
216 | 0 | { |
217 | | // if --log-level was not used... |
218 | 0 | if (level == dcmtk::log4cplus::NOT_SET_LOG_LEVEL) |
219 | 0 | level = defaultLevel; |
220 | |
|
221 | 0 | configureLogger(level); |
222 | 0 | } |
223 | |
|
224 | 0 | dcmtk::log4cplus::Logger rootLogger = dcmtk::log4cplus::Logger::getRoot(); |
225 | | // if --quiet or something equivalent was used |
226 | 0 | if (!rootLogger.isEnabledFor(OFLogger::ERROR_LOG_LEVEL)) |
227 | 0 | { |
228 | 0 | app.setQuietMode(); |
229 | 0 | dcmtk::log4cplus::helpers::LogLog::getLogLog()->setQuietMode(OFTrue); |
230 | 0 | } |
231 | 0 | else |
232 | 0 | { |
233 | 0 | dcmtk::log4cplus::helpers::LogLog::getLogLog()->setQuietMode(OFFalse); |
234 | 0 | } |
235 | | |
236 | | // print command line arguments |
237 | 0 | if (cmd.findOption("--arguments")) |
238 | 0 | { |
239 | 0 | OFStringStream stream; |
240 | 0 | stream << "calling '" << cmd.getProgramName() << "' with " << cmd.getArgCount() << " arguments: "; |
241 | 0 | const char *arg; |
242 | | // iterate over all command line arguments |
243 | 0 | if (cmd.gotoFirstArg()) |
244 | 0 | { |
245 | 0 | do { |
246 | 0 | if (cmd.getCurrentArg(arg)) |
247 | 0 | stream << "'" << arg << "' "; |
248 | 0 | } while (cmd.gotoNextArg()); |
249 | 0 | } |
250 | 0 | stream << OFendl << OFStringStream_ends; |
251 | 0 | OFSTRINGSTREAM_GETOFSTRING(stream, tmpString) |
252 | | // always output this message, i.e. without checking the log level |
253 | 0 | rootLogger.forcedLog(OFLogger::INFO_LOG_LEVEL, tmpString); |
254 | 0 | } |
255 | 0 | } |
256 | | |
257 | | void OFLog::addOptions(OFCommandLine &cmd) |
258 | 0 | { |
259 | 0 | cmd.addOption("--arguments", "print expanded command line arguments"); |
260 | 0 | cmd.addOption("--quiet", "-q", "quiet mode, print no warnings and errors"); |
261 | 0 | cmd.addOption("--verbose", "-v", "verbose mode, print processing details"); |
262 | 0 | cmd.addOption("--debug", "-d", "debug mode, print debug information"); |
263 | 0 | cmd.addOption("--log-level", "-ll", 1, "[l]evel: string constant", |
264 | 0 | "(fatal, error, warn, info, debug, trace)\nuse level l for the logger"); |
265 | 0 | cmd.addOption("--log-config", "-lc", 1, "[f]ilename: string", |
266 | 0 | "use config file f for the logger"); |
267 | 0 | } |