ArmarXFileLogger.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of ArmarX.
3  *
4  * ArmarX is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2 as
6  * published by the Free Software Foundation.
7  *
8  * ArmarX is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program. If not, see <http://www.gnu.org/licenses/>.
15  *
16  * @package ArmarXCore::ArmarXObjects::ArmarXFileLogger
17  * @author Mirko Waechter ( mirko dot waechter at kit dot edu )
18  * @date 2017
19  * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
20  * GNU General Public License
21  */
22 
23 #include "ArmarXFileLogger.h"
24 
25 #include <boost/format.hpp>
26 #include <boost/range/adaptor/reversed.hpp>
27 
32 
33 namespace armarx
34 {
35 
38  {
39  //defineRequiredProperty<std::string>("PropertyName", "Description");
40  defineOptionalProperty<int>("MaxLogFileCount",
41  100,
42  "Number of log files to keep before starting to delete old "
43  "files. If zero, no file will be deleted.");
44  defineOptionalProperty<std::string>(
45  "LogFileName", "ArmarXLog", "Logfilename without ending");
46  defineOptionalProperty<std::string>("LogFileEnding", "log", "Logfilename ending");
47  defineOptionalProperty<std::string>(
48  "LogDir",
49  "",
50  "If set, log will be stored in this path. If not set, log will be stored "
51  "in $ARMARX_USER_CONFIG_DIR/log/ (or $HOME/.armarx/log/ if $ARMARX_USER_CONFIG_DIR is "
52  "not set).");
53  defineOptionalProperty<std::string>("LogTopicName", "Log", "Name of the topic to be used");
54  defineOptionalProperty<bool>(
55  "SplitByApplication", true, "Create additional log files for each application");
56  }
57 
58  void
60  {
61  std::string directoryStr;
62  if (getProperty<std::string>("LogDir").isSet())
63  {
64  directoryStr = getProperty<std::string>("LogDir").getValue();
65  ArmarXDataPath::ReplaceEnvVars(directoryStr);
66  }
67  else if (const char* workspace = std::getenv("ARMARX_USER_CONFIG_DIR"))
68  {
69  directoryStr = (std::filesystem::path(workspace) / "log").string();
70  }
71  else
72  {
73  directoryStr = std::string("$HOME/.armarx/log/");
74  ArmarXDataPath::ReplaceEnvVars(directoryStr);
75  }
76 
77  auto directory = remove_trailing_separator(directoryStr);
78  ARMARX_DEBUG << "Checking " << directory.string();
79  if (!std::filesystem::exists(directory))
80  {
81  ARMARX_INFO << "Log path not found - creating it: " << directory.string();
82  std::error_code errcode;
83  std::filesystem::create_directories(directory, errcode);
84  if (errcode)
85  {
86  ARMARX_ERROR << "Creating directories failed: " << errcode.message();
87  return;
88  }
89  }
90 
91  auto ending = getProperty<std::string>("LogFileEnding").getValue();
92  auto fileBasename = getProperty<std::string>("LogFileName").getValue();
94  directoryStr, getProperty<int>("MaxLogFileCount").getValue(), fileBasename, ending);
95 
96 
97  if (getProperty<bool>("SplitByApplication").getValue())
98  {
99  auto filePath = std::filesystem::path(filename);
100  directory /= filePath.stem();
101  if (!std::filesystem::exists(directory))
102  {
103  ARMARX_INFO << "Log path not found - creating it: " << directory;
104  std::error_code errcode;
105  std::filesystem::create_directories(directory, errcode);
106  if (errcode)
107  {
108  ARMARX_ERROR << "Creating directories failed: " << errcode.message();
109  return;
110  }
111  }
113  directory.string() + "/" + filePath.stem().string() + "-%1%." + ending;
114  }
115 
116  // FileSystemPathBuilder builder(getProperty<std::string>("LogFileName").getValue());
117  auto logfilePath = std::filesystem::path(directoryStr) / std::filesystem::path(filename);
118  fileStream.open(logfilePath.string().c_str(), std::ofstream::out);
119  if (!fileStream.is_open())
120  {
121  ARMARX_ERROR << "Failed to open " << logfilePath.string();
122  return;
123  }
124  ARMARX_INFO << "Logging to " << logfilePath.string();
125  fileStream << "Log starting at " << IceUtil::Time::now().toDateTime() << ", "
126  << "with log name " << filename << "\n";
127  usingTopic(getProperty<std::string>("LogTopicName").getValue());
128  }
129 
130  void
132  {
133  }
134 
135  void
137  {
138  std::unique_lock lock(mutex);
140  fileStream.close();
141  applicationFileStreams.clear();
142  }
143 
144  void
146  {
147  }
148 
151  {
154  }
155 
156  std::vector<std::string>
157  ArmarXFileLogger::getDirListOrderedByDate(const std::string& dir) const
158  {
159  std::vector<std::string> result;
160  namespace fs = std::filesystem;
161  fs::path someDir(dir);
162  fs::directory_iterator end_iter;
163 
164  using result_set_t = std::multimap<std::filesystem::file_time_type, fs::path>;
165  result_set_t result_set;
166 
167  if (fs::exists(someDir) && fs::is_directory(someDir))
168  {
169  for (fs::directory_iterator dir_iter(someDir); dir_iter != end_iter; ++dir_iter)
170  {
171  if (fs::is_regular_file(dir_iter->status()))
172  {
173  result_set.insert(
174  result_set_t::value_type(fs::last_write_time(dir_iter->path()), *dir_iter));
175  }
176  }
177  }
178  for (auto& elem : boost::adaptors::reverse(result_set))
179  {
180  result.push_back(elem.second.string());
181  }
182  return result;
183  }
184 
185  std::string
187  int maxLogFileCount,
188  const std::string& fileBasename,
189  const std::string& fileNameEnding) const
190  {
191  auto files = getDirListOrderedByDate(dir);
192  // ARMARX_INFO << files;
193  std::string connectorString = "-";
194  const boost::regex e(fileBasename + connectorString + "([0-9]+)\\.");
195  int trials = 0;
196  int highestNumber = 0;
197  for (auto file : files)
198  {
199  boost::match_results<std::string::const_iterator> what;
200  file = std::filesystem::path(file).filename().string();
201  ARMARX_DEBUG << file;
202  bool found_match = boost::regex_search(file, what, e);
203  if (found_match)
204  {
205  for (size_t i = 1; i < what.size(); i += 2)
206  {
207  std::string numberStr = what[i];
208  int number = atoi(numberStr.c_str());
209  highestNumber = std::max(number, highestNumber);
210  // ARMARX_INFO << "testing number " << number << VAROUT( trials) << VAROUT(highestNumber);
211  auto filenameCandidate = fileBasename + connectorString +
212  ValueToString(number) + "." + fileNameEnding;
213  if (std::filesystem::exists(dir + "/" + filenameCandidate))
214  {
215  // ARMARX_INFO << "File exists " << VAROUT(filenameCandidate) << VAROUT(trials);
216  if (trials >= maxLogFileCount && maxLogFileCount > 0)
217  {
218  ARMARX_VERBOSE << "Deleting old log " << filenameCandidate;
219  std::filesystem::remove(dir + "/" + filenameCandidate);
220  }
221  }
222  trials++;
223  }
224  }
225  }
226  highestNumber++;
227  return fileBasename + connectorString + ValueToString(highestNumber) + "." + fileNameEnding;
228  }
229 
230  std::string
232  {
233  return "ArmarXFileLogger";
234  }
235 
236  void
237  armarx::ArmarXFileLogger::writeLog(const LogMessage& msg, const Ice::Current&)
238  {
239  std::string catStr;
240 
241  if (!msg.tag.empty())
242  {
243  catStr = "[" + msg.tag + "]: ";
244  }
245  else
246  {
247  catStr = "[" + LogSender::CropFunctionName(msg.function) + "]: ";
248  }
249  IceUtil::Time time = IceUtil::Time::microSeconds(msg.time);
250  std::unique_lock lock(mutex);
251  if (!fileStream.is_open())
252  {
253  return;
254  }
255 
256  std::string logLevel;
257  switch (msg.type)
258  {
259  case armarx::MessageType::eUNDEFINED:
260  logLevel = "UNDEFINED";
261  break;
262  case armarx::MessageType::eDEBUG:
263  logLevel = "DEBUG";
264  break;
265  case armarx::MessageType::eVERBOSE:
266  logLevel = "VERBOSE";
267  break;
268  case armarx::MessageType::eINFO:
269  logLevel = "INFO";
270  break;
271  case armarx::MessageType::eIMPORTANT:
272  logLevel = "IMPORTANT";
273  break;
274  case armarx::MessageType::eWARN:
275  logLevel = "WARN";
276  break;
277  case armarx::MessageType::eERROR:
278  logLevel = "ERROR";
279  break;
280  case armarx::MessageType::eFATAL:
281  logLevel = "FATAL";
282  break;
283  case armarx::MessageType::eLogLevelCount:
284  logLevel = "LogLevelCount";
285  break;
286  }
287 
288  std::stringstream newLines;
289  newLines << "[" + time.toDateTime().substr(time.toDateTime().find(' ') + 1) + "]" + "[" +
290  msg.who + "]" + catStr + "[" + logLevel + "]";
291  newLines << msg.what << "\n";
292  fileStream << newLines.str();
293  if (getProperty<bool>("SplitByApplication").getValue())
294  {
295  auto it = applicationFileStreams.find(msg.who);
296  if (it == applicationFileStreams.end())
297  {
298  auto filepath = boost::format(applicationsLogFileBasePath) % msg.who;
299  it = applicationFileStreams
300  .emplace(msg.who,
301  std::shared_ptr<std::ofstream>(
302  new std::ofstream(filepath.str().c_str())))
303  .first;
304  // it = applicationFileStreams.find(msg.who);
305  if (!it->second->is_open())
306  {
307  ARMARX_WARNING << "Failed to open for logging: " << filepath.str();
308  return;
309  }
310  }
311 
312  *(it->second) << newLines.str();
313  }
314  }
315 } // namespace armarx
files
Use of this software is granted under one of the following two to be chosen freely by the user Boost Software License Version Marcin Kalicinski Permission is hereby free of to any person or organization obtaining a copy of the software and accompanying documentation covered by this and transmit the and to prepare derivative works of the and to permit third parties to whom the Software is furnished to do all subject to the including the above license this restriction and the following must be included in all copies of the in whole or in and all derivative works of the unless such copies or derivative works are solely in the form of machine executable object code generated by a source language processor THE SOFTWARE IS PROVIDED AS WITHOUT WARRANTY OF ANY EXPRESS OR INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF FITNESS FOR A PARTICULAR TITLE AND NON INFRINGEMENT IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER WHETHER IN TORT OR ARISING OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE The MIT Marcin Kalicinski Permission is hereby free of to any person obtaining a copy of this software and associated documentation files(the "Software")
armarx::ArmarXFileLogger::fileStream
std::ofstream fileStream
Definition: ArmarXFileLogger.h:77
ARMARX_VERBOSE
#define ARMARX_VERBOSE
Definition: Logging.h:180
armarx::ArmarXFileLogger::writeLog
void writeLog(const LogMessage &, const Ice::Current &) override
Definition: ArmarXFileLogger.cpp:237
armarx::LogSender::CropFunctionName
static std::string CropFunctionName(const std::string &originalFunctionName)
Definition: LogSender.cpp:469
armarx::remove_trailing_separator
fs::path remove_trailing_separator(fs::path p)
Definition: filesystem.h:32
armarx::ArmarXFileLogger::applicationsLogFileBasePath
std::string applicationsLogFileBasePath
Definition: ArmarXFileLogger.h:78
filesystem.h
armarx::ArmarXFileLoggerPropertyDefinitions
Definition: ArmarXFileLogger.h:38
armarx::ArmarXFileLogger::getNextLogFileNameAndDeleteOldFiles
std::string getNextLogFileNameAndDeleteOldFiles(const std::string &dir, int maxLogFileCount, const std::string &fileBasename, const std::string &fileNameEnding) const
Definition: ArmarXFileLogger.cpp:186
StringHelpers.h
armarx::reverse
T reverse(const T &o)
Definition: container.h:32
ArmarXFileLogger.h
armarx::ArmarXFileLogger::getDirListOrderedByDate
std::vector< std::string > getDirListOrderedByDate(const std::string &dir) const
Definition: ArmarXFileLogger.cpp:157
armarx::ArmarXFileLogger::onDisconnectComponent
void onDisconnectComponent() override
Hook for subclass.
Definition: ArmarXFileLogger.cpp:136
armarx::ValueToString
std::string ValueToString(const T &value)
Definition: StringHelpers.h:58
armarx::flush
const LogSender::manipulator flush
Definition: LogSender.h:251
ARMARX_DEBUG
#define ARMARX_DEBUG
Definition: Logging.h:177
armarx::ArmarXFileLogger::mutex
std::mutex mutex
Definition: ArmarXFileLogger.h:80
filename
std::string filename
Definition: VisualizationRobot.cpp:84
max
T max(T t1, T t2)
Definition: gdiam.h:48
ARMARX_ERROR
#define ARMARX_ERROR
Definition: Logging.h:189
armarx::ArmarXFileLogger::createPropertyDefinitions
armarx::PropertyDefinitionsPtr createPropertyDefinitions() override
Creates the property definition container.
Definition: ArmarXFileLogger.cpp:150
armarx::armem::Time
armarx::core::time::DateTime Time
Definition: forward_declarations.h:13
armarx::ArmarXFileLogger::onConnectComponent
void onConnectComponent() override
Pure virtual hook for the subclass.
Definition: ArmarXFileLogger.cpp:131
armarx::ArmarXFileLogger::getDefaultName
std::string getDefaultName() const override
Retrieve default name of component.
Definition: ArmarXFileLogger.cpp:231
armarx::ArmarXFileLoggerPropertyDefinitions::ArmarXFileLoggerPropertyDefinitions
ArmarXFileLoggerPropertyDefinitions(std::string prefix)
Definition: ArmarXFileLogger.cpp:36
armarx::ManagedIceObject::usingTopic
void usingTopic(const std::string &name, bool orderedPublishing=false)
Registers a proxy for subscription after initialization.
Definition: ManagedIceObject.cpp:248
armarx::Component::getConfigIdentifier
std::string getConfigIdentifier()
Retrieve config identifier for this component as set in constructor.
Definition: Component.cpp:74
armarx::ArmarXFileLogger::onExitComponent
void onExitComponent() override
Hook for subclass.
Definition: ArmarXFileLogger.cpp:145
armarx::ComponentPropertyDefinitions
Default component property definition container.
Definition: Component.h:70
ARMARX_INFO
#define ARMARX_INFO
Definition: Logging.h:174
IceUtil::Handle< class PropertyDefinitionContainer >
armarx::ArmarXDataPath::ReplaceEnvVars
static bool ReplaceEnvVars(std::string &string)
ReplaceEnvVars replaces environment variables in a string with their values, if the env.
Definition: ArmarXDataPath.cpp:483
armarx::ArmarXFileLogger::onInitComponent
void onInitComponent() override
Pure virtual hook for the subclass.
Definition: ArmarXFileLogger.cpp:59
armarx::ArmarXFileLogger::applicationFileStreams
std::map< std::string, std::shared_ptr< std::ofstream > > applicationFileStreams
Definition: ArmarXFileLogger.h:79
ARMARX_WARNING
#define ARMARX_WARNING
Definition: Logging.h:186
armarx::PropertyDefinitionsPtr
IceUtil::Handle< class PropertyDefinitionContainer > PropertyDefinitionsPtr
PropertyDefinitions smart pointer type.
Definition: forward_declarations.h:34
ArmarXDataPath.h
FileSystemPathBuilder.h
armarx
This file offers overloads of toIce() and fromIce() functions for STL container types.
Definition: ArmarXTimeserver.cpp:28