Commit 0ef9362a authored by Cédric Traizet's avatar Cédric Traizet

Merge branch 'logging_in_python_wrapper' into 'develop'

Logging in python wrapper

See merge request !405
parents 78c3be1e a134269c
......@@ -56,6 +56,8 @@ public:
*/
static Logger * Instance();
void ResetOutputs();
static Pointer New();
itkCreateAnotherMacro( Logger )
itkCloneMacro( Logger )
......
......@@ -25,6 +25,7 @@
#include <iosfwd>
#include "otbFilterWatcherBase.h"
#include "otbStandardOutputPrintCallback.h"
namespace otb
{
......@@ -46,15 +47,16 @@ namespace otb
* StandardOneLineFilterWatcher watcher(thresholdFilter, "Threshold");
* \endcode
*
* \see otb::StandardOutputPrintCallback
* \see itk::SimpleFilterWatcher
* \see otb::fltkFilterWatcher
*
* \ingroup OTBCommon
*/
class OTBCommon_EXPORT StandardOneLineFilterWatcher : public FilterWatcherBase
template <class PrintCallbackType = StandardOutputPrintCallback>
class OTBCommon_EXPORT_TEMPLATE StandardOneLineFilterWatcher : public FilterWatcherBase
{
public:
/** Constructor. Takes a ProcessObject to monitor and an optional
* comment string that is prepended to each event message. */
StandardOneLineFilterWatcher(itk::ProcessObject* process,
......@@ -66,6 +68,9 @@ public:
/** Default constructor */
StandardOneLineFilterWatcher();
/** Destrucotr */
~StandardOneLineFilterWatcher() override = default;
/** Get/Set number of stars */
void SetStars(int count)
{
......@@ -76,6 +81,12 @@ public:
return m_StarsCount;
}
/** Set the callback class */
void SetCallback(PrintCallbackType * callback)
{
m_Callback = callback;
}
protected:
/** Callback method to show the ProgressEvent */
......@@ -89,16 +100,31 @@ protected:
private:
/** Stars coutning */
/** Stars counting */
int m_StarsCount;
/** Current number of stars, we keep track of this to avoid reprinting the
* progress if it hasn't changed */
int m_CurrentNbStars;
bool m_CoutIsConsole;
/** If the output is not interactive (e.g. it is redirected to a file), it
* is buffered and only written at the end of the processing */
std::string m_Buffer;
/** The point to the callback used for printing. It is set to the default
* callback on construction and can be changed later using the setter.
* Delete will not be called on this pointer. */
PrintCallbackType * m_Callback;
/** A default callback created in the constructor and deleted in the
* destructor. */
std::shared_ptr<PrintCallbackType> m_DefaultCallback;
};
} // end namespace otb
#ifndef OTB_MANUAL_INSTANTIATION
#include "otbStandardOneLineFilterWatcher.hxx"
#endif
#endif
......@@ -19,6 +19,9 @@
* limitations under the License.
*/
#ifndef otbStandardOneLineFilterWatcher_hxx
#define otbStandardOneLineFilterWatcher_hxx
#include <iostream>
#include <sstream>
......@@ -28,37 +31,43 @@
namespace otb
{
StandardOneLineFilterWatcher
template<class PrintCallbackType>
StandardOneLineFilterWatcher<PrintCallbackType>
::StandardOneLineFilterWatcher()
: m_StarsCount(50),
m_CurrentNbStars(-1),
m_CoutIsConsole( System::IsInteractive(1) )
m_CurrentNbStars(-1)
{
m_DefaultCallback = std::make_shared<PrintCallbackType>() ;
m_Callback = m_DefaultCallback.get();
}
StandardOneLineFilterWatcher
template<class PrintCallbackType>
StandardOneLineFilterWatcher<PrintCallbackType>
::StandardOneLineFilterWatcher(itk::ProcessObject* process,
const char *comment)
: FilterWatcherBase(process, comment),
m_StarsCount(50),
m_CurrentNbStars(-1),
m_CoutIsConsole( System::IsInteractive(1) )
m_CurrentNbStars(-1)
{
m_DefaultCallback = std::make_shared<PrintCallbackType>() ;
m_Callback = m_DefaultCallback.get();
}
StandardOneLineFilterWatcher
template<class PrintCallbackType>
StandardOneLineFilterWatcher<PrintCallbackType>
::StandardOneLineFilterWatcher(itk::ProcessObject* process,
const std::string& comment)
: FilterWatcherBase(process, comment.c_str()),
m_StarsCount(50),
m_CurrentNbStars(-1),
m_CoutIsConsole( System::IsInteractive(1) )
m_CurrentNbStars(-1)
{
m_DefaultCallback = std::make_shared<PrintCallbackType>() ;
m_Callback = m_DefaultCallback.get();
}
template<class PrintCallbackType>
void
StandardOneLineFilterWatcher
StandardOneLineFilterWatcher<PrintCallbackType>
::ShowProgress()
{
if (m_Process)
......@@ -90,9 +99,10 @@ StandardOneLineFilterWatcher
oss << m_Comment
<< ": "
<< progressPercent << "% [" << stars << blanks << "]";
if (m_CoutIsConsole)
if (m_Callback->IsInteractive())
{
std::cout << "\r" << oss.str() << std::flush;
m_Callback->Call("\r" + oss.str());
m_Callback->Flush();
}
else
{
......@@ -104,29 +114,31 @@ StandardOneLineFilterWatcher
}
}
template<class PrintCallbackType>
void
StandardOneLineFilterWatcher
StandardOneLineFilterWatcher<PrintCallbackType>
::StartFilter()
{
m_Stopwatch.Start();
}
template<class PrintCallbackType>
void
StandardOneLineFilterWatcher
StandardOneLineFilterWatcher<PrintCallbackType>
::EndFilter()
{
m_Stopwatch.Stop();
if (m_Process && !m_CoutIsConsole)
if (m_Process && !m_Callback->IsInteractive())
{
std::cout << m_Buffer;
m_Callback->Call(m_Buffer);
m_Buffer = std::string("");
}
std::cout << " (";
m_Stopwatch.GetElapsedHumanReadableTime(std::cout);
std::cout << ")"
<< std::endl;
m_Callback->Call(" (" + m_Stopwatch.GetElapsedHumanReadableTime() + ")\n");
}
} // end namespace otb
#endif
/*
* Copyright (C) 2005-2019 Centre National d'Etudes Spatiales (CNES)
*
* This file is part of Orfeo Toolbox
*
* https://www.orfeo-toolbox.org/
*
* Licensed 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.
*/
#ifndef otbStandardOutputPrintCallback_h
#define otbStandardOutputPrintCallback_h
#include "otbSystem.h"
namespace otb
{
/** \class StandardOutputPrintCallback
* \brief Class with printing callback methods using the standard output
*
* This class defines the Call method, used to write a string the standard
* output, the Flush method, used to flush it, and the IsInteractive
* method used to determine if the output is the console.
*
* \see otb::StandardOneLineFilterWatcher
*
* \ingroup OTBCommon
*/
class OTBCommon_EXPORT StandardOutputPrintCallback
{
public:
/** Constructor */
StandardOutputPrintCallback() :m_IsInteractive( System::IsInteractive(1)) {};
/** Destructor */
virtual ~StandardOutputPrintCallback() = default;
/** Write a string to a buffer */
void Call(std::string const& content);
/** Flush the buffer */
void Flush();
/** Determine if the output is interactive */
bool IsInteractive();
private:
/** flag determining if the output is interactive */
bool m_IsInteractive;
};
} // namespace otb
#endif // otbStandardOutputPrintCallback_h
......@@ -25,12 +25,12 @@ set(OTBCommon_SRC
otbStandardWriterWatcher.cxx
otbUtils.cxx
otbConfigurationManager.cxx
otbStandardOneLineFilterWatcher.cxx
otbWriterWatcherBase.cxx
otbStopwatch.cxx
otbStringToHTML.cxx
otbExtendedFilenameHelper.cxx
otbLogger.cxx
otbStandardOutputPrintCallback.cxx
)
add_library(OTBCommon ${OTBCommon_SRC})
......
......@@ -49,16 +49,14 @@ Logger * Logger::CreateInstance()
{
Logger * logger = new Logger;
// By default, redirect logs to std::cout
itk::StdStreamLogOutput::Pointer defaultOutput =
itk::StdStreamLogOutput::New();
// By default redirect logs to std::cout
itk::StdStreamLogOutput::Pointer defaultOutput = itk::StdStreamLogOutput::New();
defaultOutput->SetStream(std::cout);
logger->AddLogOutput(defaultOutput);
return logger;
}
Logger * Logger::Instance()
{
static Logger * logger_singleton = CreateInstance();
......@@ -110,6 +108,11 @@ void Logger::LogSetupInformation()
}
}
void Logger::ResetOutputs()
{
m_Output = itk::MultipleLogOutput::New();
}
bool Logger::IsLogSetupInformationDone()
{
return m_LogSetupInfoDone;
......
/*
* Copyright (C) 2005-2019 Centre National d'Etudes Spatiales (CNES)
*
* This file is part of Orfeo Toolbox
*
* https://www.orfeo-toolbox.org/
*
* Licensed 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 "otbStandardOutputPrintCallback.h"
namespace otb
{
void
StandardOutputPrintCallback
::Call(std::string const& content)
{
std::cout << content;
}
void
StandardOutputPrintCallback
::Flush()
{
std::cout << std::flush;
}
bool
StandardOutputPrintCallback
::IsInteractive()
{
return m_IsInteractive;
}
} // namespace otb
......@@ -38,7 +38,7 @@ int otbStandardOneLineFilterWatcherTest(int itkNotUsed(argc), char * argv[])
typedef itk::GradientMagnitudeImageFilter<ImageType, ImageType> FilterType;
FilterType::Pointer gradient = FilterType::New();
typedef otb::StandardOneLineFilterWatcher WatcherType;
typedef otb::StandardOneLineFilterWatcher<> WatcherType;
WatcherType watcher1(gradient, "Gradient");
gradient->SetInput(reader->GetOutput());
......
......@@ -42,7 +42,7 @@ int otbGDALOverviewsBuilder(int itkNotUsed(argc), char* argv[])
filter->SetResamplingMethod(resamp);
{
StandardOneLineFilterWatcher watcher(filter,"Overviews creation");
StandardOneLineFilterWatcher<> watcher(filter,"Overviews creation");
filter->Update();
}
......
......@@ -260,7 +260,7 @@ VectorImageModel
try
{
otb::StandardOneLineFilterWatcher watcher(
otb::StandardOneLineFilterWatcher<> watcher(
filter,
ToStdString( tr( "Overviews creation: " ) )
);
......
......@@ -76,7 +76,7 @@ public:
INVALIDNUMBEROFVALUE, DEFAULT} ParamResultType;
/** Filter watcher list type */
typedef std::vector<StandardOneLineFilterWatcher *> WatcherListType;
typedef std::vector<StandardOneLineFilterWatcher<> *> WatcherListType;
/** Command Member */
typedef itk::MemberCommand< Self > AddProcessCommandType;
......
......@@ -596,7 +596,7 @@ void CommandLineLauncher::LinkWatchers(itk::Object * itkNotUsed(caller), const i
{
const AddProcessToWatchEvent* eventToWatch = dynamic_cast<const AddProcessToWatchEvent*> (&event);
StandardOneLineFilterWatcher * watch = new StandardOneLineFilterWatcher(eventToWatch->GetProcess(),
auto watch = new StandardOneLineFilterWatcher<>(eventToWatch->GetProcess(),
eventToWatch->GetProcessDescription());
m_WatcherList.push_back(watch);
}
......
......@@ -29,6 +29,8 @@
#include "itkCommand.h"
#include "itkLogOutput.h"
typedef itk::LightObject itkLightObject;
typedef itk::LightObject::Pointer itkLightObject_Pointer;
typedef itk::Object itkObject;
......@@ -60,4 +62,6 @@ typedef itk::EndPickEvent itkEndPickEvent;
typedef itk::AbortCheckEvent itkAbortCheckEvent;
typedef itk::UserEvent itkUserEvent;
typedef itk::LogOutput itkLogOutput;
#endif
......@@ -203,6 +203,13 @@ public:
} // end of namespace otb
#if SWIGPYTHON
%include "otbPythonLogOutput.i"
#endif
class Application: public itkObject
{
public:
......@@ -217,6 +224,31 @@ public:
int Execute();
int ExecuteAndWriteOutput();
#if SWIGPYTHON
Logger* GetLogger();
#endif
unsigned long itk::Object::AddObserver(const EventObject & event,
Command * command);
#if SWIGPYTHON
%extend
{
/** SetupLogger : Add the PythonLogOutput and setup the progress
* reporting for the application */
%pythoncode
{
def SetupLogger(self):
logger = self.GetLogger()
logger.AddLogOutput(_libraryLogOutput.GetPointer())
self.AddObserver(AddProcessToWatchEvent(),
_libraryProgressReportManager.GetAddProcessCommand()
)
}
}
#endif // SWIGPYTHON
std::vector<std::string> GetParametersKeys(bool recursive = true);
Parameter* Application::GetParameterByKey(std::string name);
std::string GetParameterName(std::string);
......@@ -877,7 +909,21 @@ class Registry : public itkObject
public:
static std::vector<std::string> GetAvailableApplications();
#if SWIGPYTHON
%rename("CreateApplicationWithoutLogger") CreateApplication;
static Application_Pointer CreateApplication(const std::string& name);
%pythoncode
{
@staticmethod
def CreateApplication(name):
application = _otbApplication.Registry_CreateApplicationWithoutLogger(name)
if application is not None:
application.SetupLogger()
return application
}
#else
static Application_Pointer CreateApplication(const std::string& name);
#endif
static void AddApplicationPath(std::string newpath);
static void SetApplicationPath(std::string newpath);
static void CleanRegistry();
......
/*
* Copyright (C) 2005-2019 Centre National d'Etudes Spatiales (CNES)
*
* This file is part of Orfeo Toolbox
*
* https://www.orfeo-toolbox.org/
*
* Licensed 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.
*/
#if SWIGPYTHON
%module(directors="1") cb
%{
#include "otbPythonLogOutput.h"
#include "otbSwigPrintCallback.h"
%}
%feature("director") SwigPrintCallback;
%include "otbSwigPrintCallback.h"
class itkLogOutput : public itkObject
{
protected:
itkLogOutput();
~itkLogOutput();
};
/** This Callback class can be called from C++ to print, flush and to determine
* if stdout is redirected */
%pythoncode
{
class PythonPrintCallback(SwigPrintCallback):
def __init__(self):
super(PythonPrintCallback, self).__init__()
def Call(self, content):
sys.stdout.write(content)
def Flush(self):
sys.stdout.flush()
def IsInteractive(self):
return sys.stdout.isatty()
}
class PythonLogOutput : public itkLogOutput
{
public:
static PythonLogOutput_Pointer New();
virtual void Delete();
void SetCallback(otb::SwigPrintCallback* callback);
virtual void Write(std::string const & content);
protected:
PythonLogOutput();
};
DECLARE_REF_COUNT_CLASS( PythonLogOutput )
class Logger
{
public:
virtual void AddLogOutput(itkLogOutput *output);
static Logger * Instance();
void ResetOutputs();
protected:
Logger();
virtual ~Logger();
};
class ProgressReporterManager: public itkObject
{
public:
/** Default constructor */
static ProgressReporterManager_Pointer New();
virtual void Delete();
void DeleteWatcherList();
void SetLogOutputCallback(otb::SwigPrintCallback* callback);
itkCommand* GetAddProcessCommand();
protected:
ProgressReporterManager();
};
DECLARE_REF_COUNT_CLASS( ProgressReporterManager )
/** Create the required objects for logging. Logger.Instance() is reset in
* order to replace the itkStdStreamLogOutput by a PythonLogOutput */
%pythoncode {
_libraryLogOutput = PythonLogOutput_New()
_libraryLogCallback = PythonPrintCallback()
_libraryProgressReportManager = ProgressReporterManager_New()
Logger.Instance().ResetOutputs()
_libraryLogOutput.SetCallback(_libraryLogCallback)
Logger.Instance().AddLogOutput(_libraryLogOutput.GetPointer())
_libraryProgressReportManager.SetLogOutputCallback(_libraryLogCallback)
}
#endif
......@@ -39,4 +39,18 @@ typedef otb::Wrapper::ComplexInputImageParameter ComplexInputImageParame
typedef otb::Wrapper::ImageBaseType ImageBaseType;
#if defined(SWIGPYTHON)
#include "otbPythonLogOutput.h"
#include "otbLogger.h"
#include "otbProgressReporterManager.h"
typedef otb::Logger Logger;
typedef otb::Logger::Pointer Logger_Pointer;
typedef otb::SwigPrintCallback SwigPrintCallback;
typedef otb::PythonLogOutput PythonLogOutput;
typedef otb::PythonLogOutput::Pointer PythonLogOutput_Pointer;
typedef otb::ProgressReporterManager ProgressReporterManager;
typedef otb::ProgressReporterManager::Pointer ProgressReporterManager_Pointer;
#endif
#endif
......@@ -32,8 +32,15 @@ set(SWIG_MODULE_otbApplication_EXTRA_DEPS
${CMAKE_CURRENT_SOURCE_DIR}/../Python.i
${CMAKE_CURRENT_SOURCE_DIR}/../PyCommand.i
itkPyCommand.h
otbSwigPrintCallback.h
otbPythonLogOutput.h
otbProgressReporterManager.h
OTBApplicationEngine)
SWIG_add_module( otbApplication python ../otbApplication.i otbApplicationPYTHON_wrap.cxx itkPyCommand.cxx )
SWIG_add_module( otbApplication python ../otbApplication.i
otbApplicationPYTHON_wrap.cxx
itkPyCommand.cxx
../python/otbPythonLogOutput.cxx
../python/otbProgressReporterManager.cxx)
SWIG_link_libraries( otbApplication ${PYTHON_LIBRARIES} OTBApplicationEngine )
set_target_properties(_otbApplication PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SWIG_OUTDIR})
......
/*
* Copyright (C) 2005-2019 Centre National d'Etudes Spatiales (CNES)
*
* This file is part of Orfeo Toolbox
*
* https://www.orfeo-toolbox.org/
*
* Licensed 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 "otbProgressReporterManager.h"
#include "otbWrapperAddProcessToWatchEvent.h"
namespace otb
{
ProgressReporterManager::ProgressReporterManager()
{
// Add the callback to be added when a AddProcessToWatch event is invoked
m_AddProcessCommand = AddProcessCommandType::New();
m_AddProcessCommand->SetCallbackFunction(this, &ProgressReporterManager::LinkWatchers);
}
ProgressReporterManager::~ProgressReporterManager()
{
this->DeleteWatcherList();
}
void ProgressReporterManager::DeleteWatcherList()
{
//Delete all stored progress reporter
for (auto watcher: m_WatcherList)
{
delete watcher;
}
m_WatcherList.clear();
}
void ProgressReporterManager::LinkWatchers(itk::Object* itkNotUsed(caller), const itk::EventObject& event)
{
if (typeid(otb::Wrapper::AddProcessToWatchEvent) == typeid(event))
{
const Wrapper::AddProcessToWatchEvent* eventToWatch = dynamic_cast<const Wrapper::AddProcessToWatchEvent*>(&event);
auto watch = new WatcherType(eventToWatch->GetProcess(), eventToWatch->GetProcessDescription());
watch->SetCallback(m_Callback);
m_WatcherList.push_back(watch);
}
}
}
/*
* Copyright (C) 2005-2019 Centre National d'Etudes Spatiales (CNES)
*
* This file is part of Orfeo Toolbox