Coverage Report

Created: 2026-11-12 07:19

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}