First commit
[anna.git] / source / core / tracing / TraceWriter.cpp
1 // ANNA - Anna is Not 'N' Anymore
2 //
3 // (c) Copyright 2005-2014 Eduardo Ramos Testillano & Francisco Ruiz Rayo
4 //
5 // https://bitbucket.org/testillano/anna
6 //
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions
9 // are met:
10 //
11 //     * Redistributions of source code must retain the above copyright
12 // notice, this list of conditions and the following disclaimer.
13 //     * Redistributions in binary form must reproduce the above
14 // copyright notice, this list of conditions and the following disclaimer
15 // in the documentation and/or other materials provided with the
16 // distribution.
17 //     * Neither the name of Google Inc. nor the names of its
18 // contributors may be used to endorse or promote products derived from
19 // this software without specific prior written permission.
20 //
21 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 //
33 // Authors: eduardo.ramos.testillano@gmail.com
34 //          cisco.tierra@gmail.com
35
36
37 #include <stdarg.h>
38 #include <stdio.h>
39 #include <iostream>
40
41 #include <limits.h>
42 #include <unistd.h>
43 #include <stdlib.h>
44 #include <fcntl.h>
45 #include <sys/stat.h>
46 #include <sys/types.h>
47
48 #include <anna/core/tracing/TraceWriter.hpp>
49 #include <anna/core/DataBlock.hpp>
50 #include <anna/core/functions.hpp>
51 #include <anna/core/Configuration.hpp>
52 #include <anna/core/tracing/TraceWriterObserver.hpp>
53
54 using namespace std;
55 using namespace anna;
56
57 TraceWriter::TraceWriter() :
58   a_maxSize(DefaultMaxKBSize),
59   a_stream(-1),
60   a_lastTime(0),
61   a_observed(false) {
62   string file;
63   file = "/var/tmp/anna.process.";
64   file += functions::asString((int) getpid());
65   file += ".log";
66   setup(file.c_str(), DefaultMaxKBSize, false);
67 }
68
69 TraceWriter::TraceWriter(const char* fileName, const int maxSize) :
70   a_maxSize(DefaultMaxKBSize),
71   a_stream(-1),
72   a_lastTime(0),
73   a_observed(false) {
74   setup(fileName, maxSize, false);
75 }
76
77 void TraceWriter::setup(const char* fileName, const Configuration& configuration)
78 throw() {
79   int maxSize = a_maxSize;
80   bool clean = true;
81
82   try {
83     try {
84       maxSize = (configuration.getIntegerValue("Trace", "MaxFileSize") << 10);
85     } catch(...) {
86     }
87
88     try {
89       Logger::setLevel(Logger::asLevel(configuration.getValue("Trace", "Level")));
90     } catch(Exception& ex) {
91       ex.trace();
92     }
93
94     try {
95       clean = (configuration.getIntegerValue("Trace", "Clean") == 1);
96     } catch(...) {
97     }
98   } catch(RuntimeException&) {
99   }
100
101   setup(fileName, maxSize, clean);
102 }
103
104 void TraceWriter::setup(const char* fileName, const int maxSize, const bool clean)
105 throw() {
106   if(a_stream != -1) {
107     if(clean == false) {
108       string msg("\nTraces go on at: ");
109       msg += fileName;
110       msg += '\n';
111       write(a_stream, msg.c_str(), msg.length());
112     }
113
114     if(a_observed == true) {
115       for(observer_iterator ii = observer_begin(), maxii = observer_end(); ii != maxii; ii ++)
116         observer(ii)->handleClose(a_stream);
117     }
118
119     close(a_stream);
120     a_stream = -1;
121
122     if(clean == true) {
123       unlink(a_outputFile.c_str());
124       unlink(a_outputOldFile.c_str());
125     }
126   }
127
128   a_outputFile = fileName;
129   a_outputOldFile = a_outputFile;
130   a_outputOldFile += ".old";
131
132   if(maxSize >= (256 * 1024))
133     a_maxSize = maxSize;
134
135   const char* aux = Logger::asString(Logger::getLevel());
136   char date [anna::functions::DateTimeSizeString + 7];
137   anna_strcpy(date, "- [");
138   anna_strcat(anna_strcat(date, getDate()), "]\n");
139   int stream = prepareOutput(date);
140
141   if(stream != ErrorStream) {
142     const char* aux = "\n--------------------------------------------------------------\n";
143     const char* aux2 = "- Current trace level: ";
144     write(stream, aux, anna_strlen(aux));
145     write(stream, date, anna_strlen(date));
146     write(stream, aux2, anna_strlen(aux2));
147     aux2 = Logger::asString(Logger::getLevel());
148     write(stream, aux2, anna_strlen(aux2));
149     string filesize = functions::asString("\n- Max file size : %d Kb", a_maxSize >> 10);
150     aux2 = filesize.c_str();
151     write(stream, aux2, anna_strlen(aux2));
152     write(stream, aux, anna_strlen(aux));
153   }
154 }
155
156 void TraceWriter::attach(TraceWriterObserver* observer)
157 throw() {
158   if(observer != NULL) {
159     a_observed = true;
160     a_observers.push_back(observer);
161
162     if(a_stream != -1)
163       observer->handleOpen(a_stream);
164   }
165 }
166
167 void TraceWriter::printResume()
168 throw() {
169   cout << "Traces file ..................................: " << a_outputFile << endl;
170   cout << "Backup copy  .................................: " << a_outputOldFile << endl;
171   cout << "Files size (Kbytes) ..........................: " << (a_maxSize >> 10) << endl;
172   cout << "Traces level .................................: " << Logger::asString(Logger::getLevel()) << endl << endl;
173 }
174
175 void TraceWriter::do_write(int level, const char* text, ...)
176 throw() {
177   va_list ap;
178   const char* data;
179   int size;
180   int nbytes;
181   GuardNoLog guard(a_mutex);
182   DataBlock& dataBlock(Logger::Writer::getDataBlock());
183   data = dataBlock.getData();
184   size = dataBlock.getMaxSize();
185
186   if(size == 0) {
187     dataBlock.allocate(size = 1024);
188     data = dataBlock.getData();
189   }
190
191   while(true) {
192     va_start(ap, text);
193     nbytes = vsnprintf(const_cast <char*>(data), size, text, ap);
194
195     if(nbytes >= size) {
196       dataBlock.allocate(nbytes + 1);
197       data = dataBlock.getData();
198       size = dataBlock.getMaxSize();
199       continue;
200     }
201
202     break;
203   }
204
205   va_end(ap);
206   const char* aux = Logger::asString(Logger::Level(level));
207   char date [anna::functions::DateTimeSizeString + 5];
208   date [0] = '[';
209   date [1] = 0;
210   anna_strcat(anna_strcat(date, getDate()), "] ");
211   a_stream = prepareOutput(date);
212
213   if(a_stream == ErrorStream) {
214     cerr << Logger::asString(Logger::Level(level)) << " | " << date << " | " << data << endl << endl;
215     return;
216   }
217
218   bool ok(false);
219
220   if(write(a_stream, date, anna_strlen(date)) > 0)
221     if(write(a_stream, aux, anna_strlen(aux)) > 0)
222       if(write(a_stream, " | ", 3) > 0)
223         if(write(a_stream, data, nbytes) > 0)
224           ok = (write(a_stream, "\n\n", 2) > 0);
225
226   if(ok == false && errno != EINTR)
227     perror("Cannot save traces");
228 }
229
230 const char* TraceWriter::getDate()
231 throw() {
232   const Microsecond msnow = functions::hardwareClock();
233
234   if(a_lastTime == 0 || (msnow - a_lastTime) > 1000000L) {
235     a_lastTime = msnow;
236     char date [functions::DateTimeSizeString];
237     a_date = functions::asDateTime(functions::second(), date);
238   }
239
240   return a_date.c_str();
241 }
242
243 int TraceWriter::prepareOutput(const char* date)
244 throw() {
245   int result = a_stream;
246
247   if(result == ErrorStream)
248     return result;
249
250   if(result != -1) {
251     struct stat data;
252     int r;
253     anna_signal_shield(r, stat(a_outputFile.c_str(), &data));
254
255     if(r == -1 || data.st_size > a_maxSize) {
256       if(a_observed == true) {
257         for(observer_iterator ii = observer_begin(), maxii = observer_end(); ii != maxii; ii ++)
258           observer(ii)->handleClose(a_stream);
259       }
260
261       anna_signal_shield(r, close(result));
262       rename(a_outputFile.c_str(), a_outputOldFile.c_str());
263       result = -1;
264     }
265   }
266
267   if(result == -1) {
268     anna_signal_shield(
269       result, open(a_outputFile.c_str(), O_RDWR | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
270     );
271
272     if(result == -1) {
273       cerr << "Cannot open the file '" << a_outputFile << "': " << strerror(errno) << endl;
274       Logger::setLevel(Logger::Error);
275       result = ErrorStream;
276     } else {
277       int r;
278       anna_signal_shield(r, fcntl(result, F_GETFL));
279
280       if(r != -1) {
281         const int opts = r | O_NONBLOCK;
282         anna_signal_shield(r, fcntl(result, F_SETFL, opts));
283       }
284     }
285
286     if(a_observed == true) {
287       for(observer_iterator ii = observer_begin(), maxii = observer_end(); ii != maxii; ii ++)
288         observer(ii)->handleOpen(result);
289     }
290   }
291
292   return result;
293 }
294