File: oflog.cc

package info (click to toggle)
dcmtk 3.6.9-6
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 95,648 kB
  • sloc: ansic: 426,874; cpp: 318,177; makefile: 6,401; sh: 4,341; yacc: 1,026; xml: 482; lex: 321; perl: 277
file content (261 lines) | stat: -rw-r--r-- 9,552 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
/*
 *
 *  Copyright (C) 2009-2019, OFFIS e.V.
 *  All rights reserved.  See COPYRIGHT file for details.
 *
 *  This software and supporting documentation were developed by
 *
 *    OFFIS e.V.
 *    R&D Division Health
 *    Escherweg 2
 *    D-26121 Oldenburg, Germany
 *
 *
 *  Module:  oflog
 *
 *  Author:  Uli Schlachter
 *
 *  Purpose: Simplify the usage of log4cplus to other modules
 *
 */

#include "dcmtk/config/osconfig.h"    /* make sure OS specific configuration is included first */
#include "dcmtk/oflog/oflog.h"

#include "dcmtk/ofstd/ofstd.h"
#include "dcmtk/ofstd/ofdate.h"
#include "dcmtk/ofstd/oftime.h"
#include "dcmtk/ofstd/ofconapp.h"

#include "dcmtk/oflog/configrt.h"
#include "dcmtk/oflog/consap.h"
#include "dcmtk/oflog/helpers/loglog.h"
#include "dcmtk/oflog/helpers/socket.h"
#include "dcmtk/oflog/helpers/strhelp.h"
#include "dcmtk/oflog/internal/internal.h"

OFunique_ptr<dcmtk::log4cplus::helpers::Properties> OFLog::configProperties_;

OFLogger::OFLogger(const dcmtk::log4cplus::Logger &base)
    : dcmtk::log4cplus::Logger(base)
{
}

static void OFLog_init()
{
    // we don't have to protect against threads here, this function is guaranteed
    // to be called at least once before main() starts -> no threads yet
    static int initialized = 0;
    if (initialized)
        return;
    initialized = 1;

#ifdef HAVE_WINSOCK_H
    // initialize Winsock DLL in order to use gethostname() and the like
    WSAData winSockData;
    // we need at least version 1.1
    WORD winSockVersionNeeded = MAKEWORD(1, 1);
    WSAStartup(winSockVersionNeeded, &winSockData);
#endif

    // we default to a really simple pattern: loglevel_prefix: message\n
    const char *pattern = "%P: %m%n";
    OFunique_ptr<dcmtk::log4cplus::Layout> layout(new dcmtk::log4cplus::PatternLayout(pattern));
    dcmtk::log4cplus::SharedAppenderPtr console(new dcmtk::log4cplus::ConsoleAppender(OFTrue /* logToStdErr */, OFTrue /* immediateFlush */));
    dcmtk::log4cplus::Logger rootLogger = dcmtk::log4cplus::Logger::getRoot();

    console->setLayout(OFmove(layout));
    rootLogger.addAppender(console);
    rootLogger.setLogLevel(dcmtk::log4cplus::INFO_LOG_LEVEL);
}

// private class, this class's constructor makes sure that OFLog_init() is
// called at least once before main() runs
class static_OFLog_initializer
{
 public:
     static_OFLog_initializer()
     {
         OFLog_init();
     }
} static initializer;

void OFLog::configureLogger(dcmtk::log4cplus::LogLevel level)
{
    // This assumes that OFLog_init() was already called. We keep using its
    // setup and just change the log level.
    dcmtk::log4cplus::Logger rootLogger = dcmtk::log4cplus::Logger::getRoot();
    rootLogger.setLogLevel(level);
}

OFLogger OFLog::getLogger(const char *loggerName)
{
    OFLog_init();
    // logger objects have a reference counting copy-constructor, so returning by-value is cheap
    return dcmtk::log4cplus::Logger::getInstance(loggerName);
}

/** Adds our known variables to the Properties instance
 *  @param props instance to add the variables to
 *  @param cmd command line which we use for getting the program name,
 *             may be NULL
 */
static void addVariables(dcmtk::log4cplus::helpers::Properties &props, OFCommandLine* cmd)
{
    OFString date;
    OFString time;

    // Set ${appname}, if possible
    if (cmd) {
        OFString app;
        OFStandard::getFilenameFromPath(app, cmd->getProgramName());
        props.setProperty("appname", app);
    }

    OFDate::getCurrentDate().getISOFormattedDate(date, OFFalse);
    OFTime::getCurrentTime().getISOFormattedTime(time, OFTrue, OFFalse, OFFalse, OFFalse);

    // Set some other useful variables
    props.setProperty("hostname", dcmtk::log4cplus::helpers::getHostname(OFFalse));
    props.setProperty("pid", dcmtk::log4cplus::helpers::convertIntegerToString(OFStandard::getProcessID()));
    props.setProperty("date", date);
    props.setProperty("time", time);
}

void OFLog::reconfigure(OFCommandLine *cmd)
{
    dcmtk::log4cplus::helpers::Properties *props = configProperties_.get();

    // If configProperties_ is a NULL pointer, --log-config was never used and
    // there is nothing we could parse again.
    if (!props)
        return;

    // Add some useful variables to the properties, this really just pretends
    // the user wrote "variable = value" in the config.
    addVariables(*props, cmd);

    unsigned int flags = 0;
    // Recursively expand ${vars}
    flags |= dcmtk::log4cplus::PropertyConfigurator::fRecursiveExpansion;
    // Try to look up ${vars} internally before asking the environment
    flags |= dcmtk::log4cplus::PropertyConfigurator::fShadowEnvironment;

    // Configure log4cplus based on our settings
    dcmtk::log4cplus::PropertyConfigurator conf(*props, dcmtk::log4cplus::Logger::getDefaultHierarchy(), flags);
    conf.configure();
}

void OFLog::configure(OFLogger::LogLevel level)
{
    configureLogger(level);
}

void OFLog::configureFromCommandLine(OFCommandLine &cmd,
                                     OFConsoleApplication &app,
                                     OFLogger::LogLevel defaultLevel)
{
    OFString logLevel = "";
    OFString logConfig = "";
    dcmtk::log4cplus::LogLevel level = dcmtk::log4cplus::NOT_SET_LOG_LEVEL;

    cmd.beginOptionBlock();
    if (cmd.findOption("--debug"))
        level = OFLogger::DEBUG_LOG_LEVEL;
    if (cmd.findOption("--verbose"))
        level = OFLogger::INFO_LOG_LEVEL;
    if (cmd.findOption("--quiet"))
        level = OFLogger::FATAL_LOG_LEVEL;
    cmd.endOptionBlock();

    if (cmd.findOption("--log-level"))
    {
        app.checkConflict("--log-level", "--verbose, --debug or --quiet", level != dcmtk::log4cplus::NOT_SET_LOG_LEVEL);

        app.checkValue(cmd.getValue(logLevel));
        level = dcmtk::log4cplus::getLogLevelManager().fromString(logLevel);
        if (level == dcmtk::log4cplus::NOT_SET_LOG_LEVEL)
            app.printError("Invalid log level for --log-level option");
    }

    if (cmd.findOption("--log-config"))
    {
        app.checkConflict("--log-config", "--log-level", !logLevel.empty());
        app.checkConflict("--log-config", "--verbose, --debug or --quiet", level != dcmtk::log4cplus::NOT_SET_LOG_LEVEL);

        app.checkValue(cmd.getValue(logConfig));

        // check whether config file exists at all and is readable
        if (!OFStandard::fileExists(logConfig))
            app.printError("Specified --log-config file does not exist");
        if (!OFStandard::isReadable(logConfig))
            app.printError("Specified --log-config file cannot be read");

        // There seems to be no way to get an error value here :(
        //dcmtk::log4cplus::PropertyConfigurator::doConfigure(logConfig);

        // This does the same stuff that line above would have done, but it also
        // does some sanity checks on the config file.
        configProperties_.reset(new dcmtk::log4cplus::helpers::Properties(logConfig));
        if (configProperties_->size() == 0)
            app.printError("Specified --log-config file does not contain any settings");
        if (configProperties_->getPropertySubset("log4cplus.").size() == 0)
            app.printError("Specified --log-config file does not contain any valid settings");
        if (!configProperties_->exists("log4cplus.rootLogger"))
            app.printError("Specified --log-config file does not set up log4cplus.rootLogger");

        reconfigure(&cmd);
    }
    else
    {
        // if --log-level was not used...
        if (level == dcmtk::log4cplus::NOT_SET_LOG_LEVEL)
            level = defaultLevel;

        configureLogger(level);
    }

    dcmtk::log4cplus::Logger rootLogger = dcmtk::log4cplus::Logger::getRoot();
    // if --quiet or something equivalent was used
    if (!rootLogger.isEnabledFor(OFLogger::ERROR_LOG_LEVEL))
    {
        app.setQuietMode();
        dcmtk::log4cplus::helpers::LogLog::getLogLog()->setQuietMode(OFTrue);
    }
    else
    {
        dcmtk::log4cplus::helpers::LogLog::getLogLog()->setQuietMode(OFFalse);
    }

    // print command line arguments
    if (cmd.findOption("--arguments"))
    {
        OFStringStream stream;
        stream << "calling '" << cmd.getProgramName() << "' with " << cmd.getArgCount() << " arguments: ";
        const char *arg;
        // iterate over all command line arguments
        if (cmd.gotoFirstArg())
        {
            do {
                if (cmd.getCurrentArg(arg))
                    stream << "'" << arg << "' ";
            } while (cmd.gotoNextArg());
        }
        stream << OFendl << OFStringStream_ends;
        OFSTRINGSTREAM_GETOFSTRING(stream, tmpString)
        // always output this message, i.e. without checking the log level
        rootLogger.forcedLog(OFLogger::INFO_LOG_LEVEL, tmpString);
    }
}

void OFLog::addOptions(OFCommandLine &cmd)
{
    cmd.addOption("--arguments",            "print expanded command line arguments");
    cmd.addOption("--quiet",      "-q",     "quiet mode, print no warnings and errors");
    cmd.addOption("--verbose",    "-v",     "verbose mode, print processing details");
    cmd.addOption("--debug",      "-d",     "debug mode, print debug information");
    cmd.addOption("--log-level",  "-ll", 1, "[l]evel: string constant",
                                            "(fatal, error, warn, info, debug, trace)\nuse level l for the logger");
    cmd.addOption("--log-config", "-lc", 1, "[f]ilename: string",
                                            "use config file f for the logger");
}