source: trunk/FACT++/src/DimWriteStatistics.cc@ 16741

Last change on this file since 16741 was 16476, checked in by tbretz, 11 years ago
Added descriptions; use time as service time.
File size: 9.0 KB
Line 
1//*************************************************************************************
2/** @class DimWriteStatistics
3
4 @brief provides a statistics service telling the free space on disk and the total size written so far
5
6*/
7//*************************************************************************************
8#include "DimWriteStatistics.h"
9
10#include <sys/statvfs.h> //for getting disk free space
11#include <sys/stat.h> //for getting files sizes
12
13#include <boost/filesystem.hpp>
14
15#include "Time.h"
16
17using namespace std;
18using namespace boost::posix_time;
19
20// --------------------------------------------------------------------------
21//
22//! Constructor with correct service name. The state machine using this object should give it
23//! its own name as a parameter
24//! @param serverName the name of the server which created this object
25//
26DimWriteStatistics::DimWriteStatistics(const string& server, MessageImp &log) :
27 fLog(log),
28 fDimService(server + "/STATS", "X:4",
29 "Statistics about size written"
30 "|FreeSpace[bytes]:Free space on disk"
31 "|Written[bytes]:Bytes written in total"
32 "|Rate[bytes]:Bytes written since last update"
33 "|Elapsed[ms]:Milliseconds elapsed since last update"),
34 fCurrentFolder("."),
35 fUpdateInterval(1000),
36 fBaseSize(0),
37 fDebug(false)
38{
39 fThread = boost::thread(boost::bind(&DimWriteStatistics::UpdateService, this));
40}
41
42// --------------------------------------------------------------------------
43//
44//! Destructor. Stop thread by setting fUpdateInterval to 0 and join the
45//! thread.
46//
47DimWriteStatistics::~DimWriteStatistics()
48{
49 fUpdateInterval = 0;
50
51 // This blocks for fPeriod duration, but maybe canceling the thread
52 // could be more dangerous leaving Dim in an undefined state.
53 fThread.interrupt();
54}
55
56int DimWriteStatistics::Write(const Time &t, const string &txt, int qos)
57{
58 return fLog.Write(t, txt, qos);
59}
60
61// --------------------------------------------------------------------------
62//
63//! Retrieves the free space of the current base path
64//! @return the available free space on disk, in bytes
65//
66int64_t DimWriteStatistics::GetFreeSpace()
67{
68 struct statvfs vfs;
69 if (statvfs(fCurrentFolder.c_str(), &vfs))
70 return -1;
71
72 return vfs.f_bsize*vfs.f_bavail;
73}
74
75// --------------------------------------------------------------------------
76//
77//! Retrieves the size on disk of a given file, in bytes
78//! @param file the filename for which the size should be retrieved
79//! @return the size of the file, in bytes
80//
81int64_t DimWriteStatistics::GetFileSizeOnDisk(const string& file, MessageImp &log)
82{
83 errno = 0;
84 struct stat st;
85 if (!stat(file.c_str(), &st))
86 return st.st_size;
87
88 //ignoring error #2: no such file or directory is not an error for new files
89 if (errno == 0 || errno == 2)
90 return 0;
91
92 ostringstream str;
93 str << "stat() failed for '" << file << "': " << strerror(errno) << " [errno=" << errno << "]";
94 log.Error(str);
95
96 return -1;
97}
98
99// --------------------------------------------------------------------------
100//
101//! Check if a given path exists
102//! @param path the path to be checked
103//! @return whether or not the given path exists
104//
105bool DimWriteStatistics::DoesPathExist(string path, MessageImp &log)
106{
107 namespace fs = boost::filesystem;
108
109 if (path.empty())
110 path = ".";
111
112 const fs::path fullPath = fs::system_complete(fs::path(path));
113
114 if (!fs::exists(fullPath))
115 return false;
116
117 if (!fs::is_directory(fullPath))
118 {
119 log.Error("Path given for checking '" + path + "' designate a file name. Please provide a path name only");
120 return false;
121 }
122
123 if (access(path.c_str(), R_OK|W_OK|X_OK) != 0)
124 {
125 log.Error("Missing read, write or execute permissions on directory '" + path + "'");
126 return false;
127 }
128
129 return true;
130}
131
132// --------------------------------------------------------------------------
133//
134//! Check if a given path exists
135//! @param path the path to be checked
136//! @return whether or not the creation has been successfull
137//
138void DimWriteStatistics::CreateDirectory(string path)
139{
140 namespace fs = boost::filesystem;
141
142 //remove last '/', if present
143 if (path[path.size()-1] == '/')
144 path = path.substr(0, path.size()-1);
145
146 //create boost path
147 const fs::path fullPath = fs::system_complete(fs::path(path));
148
149 //if path does not exist, check if upper levels exist already
150 if (fs::exists(fullPath))
151 {
152 //if path already exist, make sure it does not designate a file (filenames cannot be checked if they do not exist)
153 if (fs::is_directory(fullPath))
154 return;
155
156 throw runtime_error("Path to be created is a file '" + path + "'");
157 }
158
159 // we're hitting "/", which SHOULD have existed...
160 if (path.size() <= 1)
161 throw runtime_error("Path to be created looks suspicious '"+path+"'");
162
163 if (path.find_last_of('/')!=string::npos)
164 CreateDirectory(path.substr(0, path.find_last_of('/')));
165
166 //path does not exist, and upper level have been created already by recusrion.
167 const mode_t rightsMask = S_IRWXU | S_IXGRP | S_IRGRP | S_IXOTH | S_IROTH; //everybody read, owner writes
168
169 if (mkdir(path.c_str(), rightsMask)==0)
170 return;
171
172 ostringstream str;
173 str << "mkdir() failed for '" << path << "': " << strerror(errno) << " [errno=" << errno << "]";
174 throw runtime_error(str.str());
175}
176
177// --------------------------------------------------------------------------
178//
179//! Sets the current folder
180//! @param folder the path to the folder
181//
182bool DimWriteStatistics::SetCurrentFolder(const string& folder)
183{
184 struct statvfs vfs;
185 if (statvfs(folder.empty()?".":folder.c_str(), &vfs))
186 {
187 fLog.Error("statvfs() failed for '"+folder+"'... ignoring it.");
188 return false;
189 }
190
191 fCurrentFolder = folder.empty()?".":folder;
192 return true;
193}
194
195// --------------------------------------------------------------------------
196//
197//! Updates the service. This is the function executed by the thread
198//
199void DimWriteStatistics::UpdateService()
200{
201 Time previousTime;
202 uint64_t previousSize = 0;
203
204 while (1)
205 {
206 if (fUpdateInterval==0)
207 {
208 boost::this_thread::interruption_point();
209 boost::this_thread::yield();
210 continue;
211 }
212
213 Stats data;
214
215 for (set<string>::const_iterator it = fOpenedFiles.begin(); it != fOpenedFiles.end(); it++)
216 data.sizeWritten += GetFileSizeOnDisk(*it);
217 data.sizeWritten -= fBaseSize;
218
219 const Time cTime = Time();
220
221 data.freeSpace = GetFreeSpace();
222 data.rateWritten = data.sizeWritten-previousSize;
223 data.timeElapsed = (cTime - previousTime).total_milliseconds();
224
225 previousSize = data.sizeWritten;
226 previousTime = cTime;
227
228 fDimService.setData(data);
229 fDimService.Update(cTime);
230
231 fStats = data;
232
233 if (fDebug)
234 {
235 ostringstream str;
236 str << "Written: " << fStats.sizeWritten/1000 << " kB; writing rate: ";
237 str << fStats.rateWritten/fStats.timeElapsed << " kB/s; free space: ";
238 str << fStats.freeSpace/1000000 << " MB";
239 fLog.Debug(str);
240 }
241
242 boost::this_thread::sleep(milliseconds(fUpdateInterval));
243 }
244}
245// --------------------------------------------------------------------------
246//
247//! Let the object know that a new file has been opened
248//! @param fileName the full name of the file newly opened
249//! @return whether this file could be stated or not
250//
251bool DimWriteStatistics::FileOpened(const string& fileName)
252{
253 if (fOpenedFiles.find(fileName) != fOpenedFiles.end())
254 return false;
255
256 //Add a newly opened file, and remember its original size
257 const int64_t newSize = GetFileSizeOnDisk(fileName);
258 if (newSize == -1)
259 return false;
260
261 fBaseSize += newSize;
262 fOpenedFiles.insert(fileName);
263
264 return true;
265}
266// --------------------------------------------------------------------------
267//
268//! Set the debug mode on and off
269//! @param debug the new mode (true or false)
270//
271void DimWriteStatistics::SetDebugMode(bool debug)
272{
273 fDebug = debug;
274
275 if (fDebug)
276 fLog.Debug("Debug mode is now on.");
277}
278// --------------------------------------------------------------------------
279//
280//! Set the update of the service interval
281//! @param duration the duration between two services update, in second
282//
283void DimWriteStatistics::SetUpdateInterval(const int16_t duration)
284{
285 if (!finite(duration))
286 {
287 fLog.Error("Provided update interval is not a valid float... discarding.");
288 return;
289 }
290 if (uint16_t(duration) == fUpdateInterval)
291 {
292 fLog.Warn("Statistics update interval not modified. Supplied value already in use.");
293 return;
294 }
295
296 if (duration <= 0)
297 fLog.Message("Statistics are now OFF.");
298 else
299 {
300 ostringstream str;
301 str << "Statistics update interval is now " << duration << " seconds";
302 fLog.Message(str);
303 }
304
305 fUpdateInterval = duration<0 ? 0 : duration;
306}
Note: See TracBrowser for help on using the repository browser.