239db1cb181318b3db50158770d9c11aef0b0d46
[anna.git] / source / core / tracing / TraceWriter.cpp
1 // ANNA - Anna is Not Nothingness Anymore                                                         //
2 //                                                                                                //
3 // (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo                         //
4 //                                                                                                //
5 // See project site at http://redmine.teslayout.com/projects/anna-suite                           //
6 // See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE //
7
8
9 #include <stdarg.h>
10 #include <stdio.h>
11 #include <iostream>
12
13 #include <limits.h>
14 #include <unistd.h>
15 #include <stdlib.h>
16 #include <fcntl.h>
17 #include <sys/stat.h>
18 #include <sys/types.h>
19
20 #include <anna/core/tracing/TraceWriter.hpp>
21 #include <anna/core/DataBlock.hpp>
22 #include <anna/core/functions.hpp>
23 #include <anna/core/util/Configuration.hpp>
24 #include <anna/core/tracing/TraceWriterObserver.hpp>
25
26 using namespace std;
27 using namespace anna;
28
29 TraceWriter::TraceWriter() :
30   a_maxSize(DefaultMaxKBSize),
31   a_stream(-1),
32   a_lastTime(0),
33   a_observed(false) {
34   string file;
35   file = "/var/tmp/anna.process.";
36   file += functions::asString((int) getpid());
37   file += ".log";
38   setup(file.c_str(), DefaultMaxKBSize, false);
39 }
40
41 TraceWriter::TraceWriter(const char* fileName, const int maxSize) :
42   a_maxSize(DefaultMaxKBSize),
43   a_stream(-1),
44   a_lastTime(0),
45   a_observed(false) {
46   setup(fileName, maxSize, false);
47 }
48
49 void TraceWriter::setup(const char* fileName, const Configuration& configuration)
50 throw() {
51   int maxSize = a_maxSize;
52   bool clean = true;
53
54   try {
55     try {
56       maxSize = (configuration.getIntegerValue("Trace", "MaxFileSize") << 10);
57     } catch(...) {
58     }
59
60     try {
61       Logger::setLevel(Logger::asLevel(configuration.getValue("Trace", "Level")));
62     } catch(Exception& ex) {
63       ex.trace();
64     }
65
66     try {
67       clean = (configuration.getIntegerValue("Trace", "Clean") == 1);
68     } catch(...) {
69     }
70   } catch(RuntimeException&) {
71   }
72
73   setup(fileName, maxSize, clean);
74 }
75
76 void TraceWriter::setup(const char* fileName, const int maxSize, const bool clean)
77 throw() {
78   if(a_stream != -1) {
79     if(clean == false) {
80       string msg("\nTraces go on at: ");
81       msg += fileName;
82       msg += '\n';
83       write(a_stream, msg.c_str(), msg.length());
84     }
85
86     if(a_observed == true) {
87       for(observer_iterator ii = observer_begin(), maxii = observer_end(); ii != maxii; ii ++)
88         observer(ii)->handleClose(a_stream);
89     }
90
91     close(a_stream);
92     a_stream = -1;
93
94     if(clean == true) {
95       unlink(a_outputFile.c_str());
96       unlink(a_outputOldFile.c_str());
97     }
98   }
99
100   a_outputFile = fileName;
101   a_outputOldFile = a_outputFile;
102   a_outputOldFile += ".old";
103
104   if(maxSize >= (256 * 1024))
105     a_maxSize = maxSize;
106
107   const char* aux = Logger::asString(Logger::getLevel());
108   char date [anna::functions::DateTimeSizeString + 7];
109   anna_strcpy(date, "- [");
110   anna_strcat(anna_strcat(date, getDate()), "]\n");
111   int stream = prepareOutput(date);
112
113   if(stream != ErrorStream) {
114     const char* aux = "\n--------------------------------------------------------------\n";
115     const char* aux2 = "- Current trace level: ";
116     write(stream, aux, anna_strlen(aux));
117     write(stream, date, anna_strlen(date));
118     write(stream, aux2, anna_strlen(aux2));
119     aux2 = Logger::asString(Logger::getLevel());
120     write(stream, aux2, anna_strlen(aux2));
121     string filesize = functions::asString("\n- Max file size : %d Kb", a_maxSize >> 10);
122     aux2 = filesize.c_str();
123     write(stream, aux2, anna_strlen(aux2));
124     write(stream, aux, anna_strlen(aux));
125   }
126 }
127
128 void TraceWriter::attach(TraceWriterObserver* observer)
129 throw() {
130   if(observer != NULL) {
131     a_observed = true;
132     a_observers.push_back(observer);
133
134     if(a_stream != -1)
135       observer->handleOpen(a_stream);
136   }
137 }
138
139 void TraceWriter::printResume()
140 throw() {
141   cout << "Traces file ..................................: " << a_outputFile << endl;
142   cout << "Backup copy  .................................: " << a_outputOldFile << endl;
143   cout << "Files size (Kbytes) ..........................: " << (a_maxSize >> 10) << endl;
144   cout << "Traces level .................................: " << Logger::asString(Logger::getLevel()) << endl << endl;
145 }
146
147 void TraceWriter::do_write(int level, const char* text, ...)
148 throw() {
149   va_list ap;
150   const char* data;
151   int size;
152   int nbytes;
153   GuardNoLog guard(a_mutex);
154   DataBlock& dataBlock(Logger::Writer::getDataBlock());
155   data = dataBlock.getData();
156   size = dataBlock.getMaxSize();
157
158   if(size == 0) {
159     dataBlock.allocate(size = 1024);
160     data = dataBlock.getData();
161   }
162
163   while(true) {
164     va_start(ap, text);
165     nbytes = vsnprintf(const_cast <char*>(data), size, text, ap);
166
167     if(nbytes >= size) {
168       dataBlock.allocate(nbytes + 1);
169       data = dataBlock.getData();
170       size = dataBlock.getMaxSize();
171       continue;
172     }
173
174     break;
175   }
176
177   va_end(ap);
178   const char* aux = Logger::asString(Logger::Level(level));
179   char date [anna::functions::DateTimeSizeString + 5];
180   date [0] = '[';
181   date [1] = 0;
182   anna_strcat(anna_strcat(date, getDate()), "] ");
183   a_stream = prepareOutput(date);
184
185   if(a_stream == ErrorStream) {
186     cerr << Logger::asString(Logger::Level(level)) << " | " << date << " | " << data << endl << endl;
187     return;
188   }
189
190   bool ok(false);
191
192   if(write(a_stream, date, anna_strlen(date)) > 0)
193     if(write(a_stream, aux, anna_strlen(aux)) > 0)
194       if(write(a_stream, " | ", 3) > 0)
195         if(write(a_stream, data, nbytes) > 0)
196           ok = (write(a_stream, "\n\n", 2) > 0);
197
198   if(ok == false && errno != EINTR)
199     perror("Cannot save traces");
200 }
201
202 const char* TraceWriter::getDate()
203 throw() {
204   const Microsecond msnow = functions::hardwareClock();
205
206   if(a_lastTime == 0 || (msnow - a_lastTime) > 1000000L) {
207     a_lastTime = msnow;
208     char date [functions::DateTimeSizeString];
209     a_date = functions::asDateTime(functions::second(), date);
210   }
211
212   return a_date.c_str();
213 }
214
215 int TraceWriter::prepareOutput(const char* date)
216 throw() {
217   int result = a_stream;
218
219   if(result == ErrorStream)
220     return result;
221
222   if(result != -1) {
223     struct stat data;
224     int r;
225     anna_signal_shield(r, stat(a_outputFile.c_str(), &data));
226
227     if(r == -1 || data.st_size > a_maxSize) {
228       if(a_observed == true) {
229         for(observer_iterator ii = observer_begin(), maxii = observer_end(); ii != maxii; ii ++)
230           observer(ii)->handleClose(a_stream);
231       }
232
233       anna_signal_shield(r, close(result));
234       rename(a_outputFile.c_str(), a_outputOldFile.c_str());
235       result = -1;
236     }
237   }
238
239   if(result == -1) {
240     anna_signal_shield(
241       result, open(a_outputFile.c_str(), O_RDWR | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
242     );
243
244     if(result == -1) {
245       cerr << "Cannot open the file '" << a_outputFile << "': " << strerror(errno) << endl;
246       Logger::setLevel(Logger::Error);
247       result = ErrorStream;
248     } else {
249       int r;
250       anna_signal_shield(r, fcntl(result, F_GETFL));
251
252       if(r != -1) {
253         const int opts = r | O_NONBLOCK;
254         anna_signal_shield(r, fcntl(result, F_SETFL, opts));
255       }
256     }
257
258     if(a_observed == true) {
259       for(observer_iterator ii = observer_begin(), maxii = observer_end(); ii != maxii; ii ++)
260         observer(ii)->handleOpen(result);
261     }
262   }
263
264   return result;
265 }
266