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
31
32namespace armarx
33{
34
37 {
38 //defineRequiredProperty<std::string>("PropertyName", "Description");
39 defineOptionalProperty<int>("MaxLogFileCount",
40 100,
41 "Number of log files to keep before starting to delete old "
42 "files. If zero, no file will be deleted.");
44 "LogFileName", "ArmarXLog", "Logfilename without ending");
45 defineOptionalProperty<std::string>("LogFileEnding", "log", "Logfilename ending");
47 "LogDir",
48 "",
49 "If set, log will be stored in this path. If not set, log will be stored "
50 "in $ARMARX_USER_CONFIG_DIR/log/ (or $HOME/.armarx/log/ if $ARMARX_USER_CONFIG_DIR is "
51 "not set).");
52 defineOptionalProperty<std::string>("LogTopicName", "Log", "Name of the topic to be used");
54 "SplitByApplication", true, "Create additional log files for each application");
55 }
56
57 void
59 {
60 std::string directoryStr;
61 if (getProperty<std::string>("LogDir").isSet())
62 {
63 directoryStr = getProperty<std::string>("LogDir").getValue();
65 }
66 else if (const char* workspace = std::getenv("ARMARX_USER_CONFIG_DIR"))
67 {
68 directoryStr = (std::filesystem::path(workspace) / "log").string();
69 }
70 else
71 {
72 directoryStr = std::string("$HOME/.armarx/log/");
74 }
75
76 auto directory = remove_trailing_separator(directoryStr);
77 ARMARX_DEBUG << "Checking " << directory.string();
78 if (!std::filesystem::exists(directory))
79 {
80 ARMARX_INFO << "Log path not found - creating it: " << directory.string();
81 std::error_code errcode;
82 std::filesystem::create_directories(directory, errcode);
83 if (errcode)
84 {
85 ARMARX_ERROR << "Creating directories failed: " << errcode.message();
86 return;
87 }
88 }
89
90 auto ending = getProperty<std::string>("LogFileEnding").getValue();
91 auto fileBasename = getProperty<std::string>("LogFileName").getValue();
93 directoryStr, getProperty<int>("MaxLogFileCount").getValue(), fileBasename, ending);
94
95
96 if (getProperty<bool>("SplitByApplication").getValue())
97 {
98 auto filePath = std::filesystem::path(filename);
99 directory /= filePath.stem();
100 if (!std::filesystem::exists(directory))
101 {
102 ARMARX_INFO << "Log path not found - creating it: " << directory;
103 std::error_code errcode;
104 std::filesystem::create_directories(directory, errcode);
105 if (errcode)
106 {
107 ARMARX_ERROR << "Creating directories failed: " << errcode.message();
108 return;
109 }
110 }
112 directory.string() + "/" + filePath.stem().string() + "-%1%." + ending;
113 }
114
115 // FileSystemPathBuilder builder(getProperty<std::string>("LogFileName").getValue());
116 auto logfilePath = std::filesystem::path(directoryStr) / std::filesystem::path(filename);
117 fileStream.open(logfilePath.string().c_str(), std::ofstream::out);
118 if (!fileStream.is_open())
119 {
120 ARMARX_ERROR << "Failed to open " << logfilePath.string();
121 return;
122 }
123 ARMARX_INFO << "Logging to " << logfilePath.string();
124 fileStream << "Log starting at " << IceUtil::Time::now().toDateTime() << ", "
125 << "with log name " << filename << "\n";
126 usingTopic(getProperty<std::string>("LogTopicName").getValue());
127 }
128
129 void
133
134 void
136 {
137 std::unique_lock lock(mutex);
138 fileStream << std::flush;
139 fileStream.close();
141 }
142
143 void
147
154
155 std::vector<std::string>
156 ArmarXFileLogger::getDirListOrderedByDate(const std::string& dir) const
157 {
158 std::vector<std::string> result;
159 namespace fs = std::filesystem;
160 fs::path someDir(dir);
161 fs::directory_iterator end_iter;
162
163 using result_set_t = std::multimap<std::filesystem::file_time_type, fs::path>;
164 result_set_t result_set;
165
166 if (fs::exists(someDir) && fs::is_directory(someDir))
167 {
168 for (fs::directory_iterator dir_iter(someDir); dir_iter != end_iter; ++dir_iter)
169 {
170 if (fs::is_regular_file(dir_iter->status()))
171 {
172 result_set.insert(
173 result_set_t::value_type(fs::last_write_time(dir_iter->path()), *dir_iter));
174 }
175 }
176 }
177 for (auto it = result_set.rbegin(); it != result_set.rend(); ++it)
178 {
179 result.push_back(it->second.string());
180 }
181 return result;
182 }
183
184 std::string
186 int maxLogFileCount,
187 const std::string& fileBasename,
188 const std::string& fileNameEnding) const
189 {
190 auto files = getDirListOrderedByDate(dir);
191 // ARMARX_INFO << files;
192 std::string connectorString = "-";
193 const boost::regex e(fileBasename + connectorString + "([0-9]+)\\.");
194 int trials = 0;
195 int highestNumber = 0;
196 for (auto file : files)
197 {
198 boost::match_results<std::string::const_iterator> what;
199 file = std::filesystem::path(file).filename().string();
200 ARMARX_DEBUG << file;
201 bool found_match = boost::regex_search(file, what, e);
202 if (found_match)
203 {
204 for (size_t i = 1; i < what.size(); i += 2)
205 {
206 std::string numberStr = what[i];
207 int number = atoi(numberStr.c_str());
208 highestNumber = std::max(number, highestNumber);
209 // ARMARX_INFO << "testing number " << number << VAROUT( trials) << VAROUT(highestNumber);
210 auto filenameCandidate = fileBasename + connectorString +
211 ValueToString(number) + "." + fileNameEnding;
212 if (std::filesystem::exists(dir + "/" + filenameCandidate))
213 {
214 // ARMARX_INFO << "File exists " << VAROUT(filenameCandidate) << VAROUT(trials);
215 if (trials >= maxLogFileCount && maxLogFileCount > 0)
216 {
217 ARMARX_VERBOSE << "Deleting old log " << filenameCandidate;
218 std::filesystem::remove(dir + "/" + filenameCandidate);
219 }
220 }
221 trials++;
222 }
223 }
224 }
225 highestNumber++;
226 return fileBasename + connectorString + ValueToString(highestNumber) + "." + fileNameEnding;
227 }
228
229 std::string
231 {
232 return "ArmarXFileLogger";
233 }
234
235 void
236 armarx::ArmarXFileLogger::writeLog(const LogMessage& msg, const Ice::Current&)
237 {
238 std::string catStr;
239
240 if (!msg.tag.empty())
241 {
242 catStr = "[" + msg.tag + "]: ";
243 }
244 else
245 {
246 catStr = "[" + LogSender::CropFunctionName(msg.function) + "]: ";
247 }
248 IceUtil::Time time = IceUtil::Time::microSeconds(msg.time);
249 std::unique_lock lock(mutex);
250 if (!fileStream.is_open())
251 {
252 return;
253 }
254
255 std::string logLevel;
256 switch (msg.type)
257 {
258 case armarx::MessageType::eUNDEFINED:
259 logLevel = "UNDEFINED";
260 break;
261 case armarx::MessageType::eDEBUG:
262 logLevel = "DEBUG";
263 break;
264 case armarx::MessageType::eVERBOSE:
265 logLevel = "VERBOSE";
266 break;
267 case armarx::MessageType::eINFO:
268 logLevel = "INFO";
269 break;
270 case armarx::MessageType::eIMPORTANT:
271 logLevel = "IMPORTANT";
272 break;
273 case armarx::MessageType::eWARN:
274 logLevel = "WARN";
275 break;
276 case armarx::MessageType::eERROR:
277 logLevel = "ERROR";
278 break;
279 case armarx::MessageType::eFATAL:
280 logLevel = "FATAL";
281 break;
282 case armarx::MessageType::eLogLevelCount:
283 logLevel = "LogLevelCount";
284 break;
285 }
286
287 std::stringstream newLines;
288 newLines << "[" + time.toDateTime().substr(time.toDateTime().find(' ') + 1) + "]" + "[" +
289 msg.who + "]" + catStr + "[" + logLevel + "]";
290 newLines << msg.what << "\n";
291 fileStream << newLines.str();
292 if (getProperty<bool>("SplitByApplication").getValue())
293 {
294 auto it = applicationFileStreams.find(msg.who);
295 if (it == applicationFileStreams.end())
296 {
297 auto filepath = boost::format(applicationsLogFileBasePath) % msg.who;
299 .emplace(msg.who,
300 std::shared_ptr<std::ofstream>(
301 new std::ofstream(filepath.str().c_str())))
302 .first;
303 // it = applicationFileStreams.find(msg.who);
304 if (!it->second->is_open())
305 {
306 ARMARX_WARNING << "Failed to open for logging: " << filepath.str();
307 return;
308 }
309 }
310
311 *(it->second) << newLines.str();
312 }
313 }
314} // namespace armarx
static bool ReplaceEnvVars(std::string &string)
ReplaceEnvVars replaces environment variables in a string with their values, if the env.
std::string applicationsLogFileBasePath
void onInitComponent() override
Pure virtual hook for the subclass.
std::vector< std::string > getDirListOrderedByDate(const std::string &dir) const
void onDisconnectComponent() override
Hook for subclass.
armarx::PropertyDefinitionsPtr createPropertyDefinitions() override
Creates the property definition container.
void onConnectComponent() override
Pure virtual hook for the subclass.
void writeLog(const LogMessage &, const Ice::Current &) override
std::map< std::string, std::shared_ptr< std::ofstream > > applicationFileStreams
std::string getNextLogFileNameAndDeleteOldFiles(const std::string &dir, int maxLogFileCount, const std::string &fileBasename, const std::string &fileNameEnding) const
void onExitComponent() override
Hook for subclass.
std::string getDefaultName() const override
Retrieve default name of component.
ComponentPropertyDefinitions(std::string prefix, bool hasObjectNameParameter=true)
Definition Component.cpp:44
std::string getConfigIdentifier()
Retrieve config identifier for this component as set in constructor.
Definition Component.cpp:88
Property< PropertyType > getProperty(const std::string &name)
static std::string CropFunctionName(const std::string &originalFunctionName)
void usingTopic(const std::string &name, bool orderedPublishing=false)
Registers a proxy for subscription after initialization.
std::string prefix
Prefix of the properties such as namespace, domain, component name, etc.
PropertyDefinition< PropertyType > & defineOptionalProperty(const std::string &name, PropertyType defaultValue, const std::string &description="", PropertyDefinitionBase::PropertyConstness constness=PropertyDefinitionBase::eConstant)
#define ARMARX_INFO
The normal logging level.
Definition Logging.h:179
#define ARMARX_ERROR
The logging level for unexpected behaviour, that must be fixed.
Definition Logging.h:194
#define ARMARX_DEBUG
The logging level for output that is only interesting while debugging.
Definition Logging.h:182
#define ARMARX_WARNING
The logging level for unexpected behaviour, but not a serious problem.
Definition Logging.h:191
#define ARMARX_VERBOSE
The logging level for verbose information.
Definition Logging.h:185
This file offers overloads of toIce() and fromIce() functions for STL container types.
fs::path remove_trailing_separator(fs::path p)
Definition filesystem.h:34
IceUtil::Handle< class PropertyDefinitionContainer > PropertyDefinitionsPtr
PropertyDefinitions smart pointer type.
std::string ValueToString(const T &value)