blob: 1654efd571ea9469838c96fa26285bca99040da9 [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <log4cxx-qt/configuration.h>
#include <log4cxx-qt/transcoder.h>
#include <log4cxx/helpers/loglog.h>
#include <log4cxx/helpers/stringhelper.h>
#include <log4cxx/xml/domconfigurator.h>
#include <log4cxx/propertyconfigurator.h>
#include <QFileSystemWatcher>
#include <QDir>
#include <QFile>
#include <memory>
#include <QDebug>
namespace
{
using namespace LOG4CXX_NS;
spi::ConfigurationStatus tryLoadFile(const LogString& lsFilename)
{
return helpers::StringHelper::endsWith(lsFilename, LOG4CXX_STR(".xml"))
? xml::DOMConfigurator::configure(lsFilename)
: PropertyConfigurator::configure(lsFilename);
}
spi::ConfigurationStatus tryLoadFile(const QString& filename)
{
LOG4CXX_DECODE_QSTRING(lsFilename, filename);
return tryLoadFile(lsFilename);
}
} // namespace
namespace LOG4CXX_NS::qt
{
void Configuration::reconfigureWhenModified(const QString& filename)
{
static auto watcher = std::make_unique<QFileSystemWatcher>();
QFileInfo fi(filename);
// From the Qt docs:
// Note that QFileSystemWatcher stops monitoring files once they have been renamed
// or removed from disk, and directories once they have been removed from disk.
//
// Some text editing programs will replace the file with a new one, which deletes
// the old file(thus causing Qt to remove the watch), so we need to add in the
// file whenever the directory changes.
// See also: https://stackoverflow.com/questions/18300376/qt-qfilesystemwatcher-signal-filechanged-gets-emited-only-once
watcher->addPath(fi.absolutePath());
if (helpers::LogLog::isDebugEnabled())
{
LOG4CXX_DECODE_QSTRING(lsDir, fi.absolutePath());
helpers::LogLog::debug(LOG4CXX_STR("Watching directory ") + lsDir);
}
watcher->addPath(fi.absoluteFilePath());
if (helpers::LogLog::isDebugEnabled())
{
LOG4CXX_DECODE_QSTRING(lsFile, fi.absoluteFilePath());
helpers::LogLog::debug(LOG4CXX_STR("Watching file ") + lsFile);
}
QObject::connect(watcher.get(), &QFileSystemWatcher::directoryChanged
, [fi](const QString&){
if (fi.exists())
watcher->addPath(fi.absoluteFilePath());
});
QObject::connect(watcher.get(), &QFileSystemWatcher::fileChanged
, [](const QString& fullPath){
tryLoadFile(fullPath);
});
}
void Configuration::reconfigureWhenModified(const LogString& lsFilename)
{
LOG4CXX_ENCODE_QSTRING(filename, lsFilename);
reconfigureWhenModified(filename);
}
std::tuple<spi::ConfigurationStatus,QString>
Configuration::configureFromFileAndWatch
( const QVector<QString>& directories
, const QVector<QString>& filenames
)
{
for( QString dir : directories )
{
for( QString fname : filenames )
{
QString candidate_str = dir + "/" + fname;
QFile candidate(candidate_str);
if (helpers::LogLog::isDebugEnabled())
{
LOG4CXX_DECODE_QSTRING(msg, "Checking file " + candidate_str);
helpers::LogLog::debug(msg);
}
if (candidate.exists())
{
auto configStatus = tryLoadFile(candidate_str);
if( configStatus == spi::ConfigurationStatus::Configured )
{
reconfigureWhenModified(candidate_str);
return {configStatus, candidate_str};
}
if (helpers::LogLog::isDebugEnabled())
{
LOG4CXX_DECODE_QSTRING(failmsg, "Unable to load " + candidate_str + ": trying next");
helpers::LogLog::debug(failmsg);
}
}
}
}
return {spi::ConfigurationStatus::NotConfigured, QString()};
}
} // namespace LOG4CXX_NS::qt