Line data Source code
1 : /********************************************************************\
2 : * TransLog.c -- the transaction logger *
3 : * Copyright (C) 1998 Linas Vepstas *
4 : * *
5 : * This program is free software; you can redistribute it and/or *
6 : * modify it under the terms of the GNU General Public License as *
7 : * published by the Free Software Foundation; either version 2 of *
8 : * the License, or (at your option) any later version. *
9 : * *
10 : * This program is distributed in the hope that it will be useful, *
11 : * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 : * GNU General Public License for more details. *
14 : * *
15 : * You should have received a copy of the GNU General Public License*
16 : * along with this program; if not, contact: *
17 : * *
18 : * Free Software Foundation Voice: +1-617-542-5942 *
19 : * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
20 : * Boston, MA 02110-1301, USA gnu@gnu.org *
21 : * *
22 : \********************************************************************/
23 :
24 : #include <config.h>
25 : #ifdef __MINGW32__
26 : #define __USE_MINGW_ANSI_STDIO 1
27 : #endif
28 : #include <errno.h>
29 : #include <glib.h>
30 : #include <glib/gstdio.h>
31 : #include <string>
32 : #include <fstream>
33 :
34 : #include "Account.h"
35 : #include "Transaction.h"
36 : #include "TransactionP.hpp"
37 : #include "TransLog.h"
38 : #include "qof.h"
39 : #ifdef _MSC_VER
40 : # define g_fopen fopen
41 : #endif
42 :
43 : static QofLogModule log_module = "gnc.translog";
44 :
45 : /*
46 : * Some design philosophy that I think would be good to keep in mind:
47 : * (0) Simplicity and foolproofness are the over-riding design points.
48 : * This is supposed to be a fail-safe safety net. We don't want
49 : * our safety net to fail because of some whiz-bang shenanigans.
50 : *
51 : * (1) Try to keep the code simple. Want to make it simple and obvious
52 : * that we are recording everything that we need to record.
53 : *
54 : * (2) Keep the printed format human readable, for the same reasons.
55 : * (2.a) Keep the format, simple, flat, more or less unstructured,
56 : * record oriented. This will help parsing by perl scripts.
57 : * No, using a perl script to analyze a file that's supposed to
58 : * be human readable is not a contradication in terms -- that's
59 : * exactly the point.
60 : * (2.b) Use tabs as a human friendly field separator; its also a
61 : * character that does not (should not) appear naturally anywhere
62 : * in the data, as it serves no formatting purpose in the current
63 : * GUI design. (hack alert -- this is not currently tested for
64 : * or enforced, so this is a very unsafe assumption. Maybe
65 : * urlencoding should be used.)
66 : * (2.c) Don't print redundant information in a single record. This
67 : * would just confuse any potential user of this file.
68 : * (2.d) Saving space, being compact is not a priority, I don't think.
69 : *
70 : * (3) There are no compatibility requirements from release to release.
71 : * Sounds OK to me to change the format of the output when needed.
72 : *
73 : * (-) print transaction start and end delimiters
74 : * (-) print a unique transaction id as a handy label for anyone
75 : * who actually examines these logs.
76 : * The C address pointer to the transaction struct should be fine,
77 : * as it is simple and unique until the transaction is deleted ...
78 : * and we log deletions, so that's OK. Just note that the id
79 : * for a deleted transaction might be recycled.
80 : * (-) print the current timestamp, so that if it is known that a bug
81 : * occurred at a certain time, it can be located.
82 : * (-) hack alert -- something better than just the account name
83 : * is needed for identifying the account.
84 : */
85 : /* ------------------------------------------------------------------ */
86 :
87 :
88 : static int gen_logs = 1;
89 : static std::ofstream trans_log_stream; /**< current log file handle */
90 : static std::string trans_log_name; /**< current log file name */
91 : static std::string log_base_name;
92 :
93 : /********************************************************************\
94 : \********************************************************************/
95 :
96 158 : void xaccLogDisable (void)
97 : {
98 158 : gen_logs = 0;
99 158 : }
100 144 : void xaccLogEnable (void)
101 : {
102 144 : gen_logs = 1;
103 144 : }
104 :
105 : /********************************************************************\
106 : \********************************************************************/
107 :
108 : void
109 0 : xaccReopenLog (void)
110 : {
111 0 : if (trans_log_stream.is_open())
112 : {
113 0 : xaccCloseLog();
114 0 : xaccOpenLog();
115 : }
116 0 : }
117 :
118 :
119 : void
120 58 : xaccLogSetBaseName (const char *basepath)
121 : {
122 58 : if (!basepath) return;
123 :
124 44 : log_base_name = basepath;
125 :
126 44 : if (trans_log_stream.is_open())
127 : {
128 15 : xaccCloseLog();
129 15 : xaccOpenLog();
130 : }
131 : }
132 :
133 :
134 : /*
135 : * See if the provided file name is that of the current log file.
136 : * Since the filename is generated with a time-stamp we can ignore the
137 : * directory path and avoid problems with worrying about any ".."
138 : * components in the path.
139 : */
140 : gboolean
141 0 : xaccFileIsCurrentLog (const gchar *name)
142 : {
143 : gchar *base;
144 :
145 0 : if (!name || trans_log_name.empty())
146 0 : return FALSE;
147 :
148 0 : base = g_path_get_basename(name);
149 0 : bool result = trans_log_name.compare(base) == 0;
150 0 : g_free(base);
151 0 : return result;
152 : }
153 :
154 : /********************************************************************\
155 : \********************************************************************/
156 :
157 : void
158 4890 : xaccOpenLog (void)
159 : {
160 : char * filename;
161 : char * timestamp;
162 :
163 4890 : if (!gen_logs)
164 : {
165 2487 : PINFO ("Attempt to open disabled transaction log");
166 2487 : return;
167 : }
168 2403 : if (trans_log_stream.is_open()) return;
169 :
170 49 : if (log_base_name.empty())
171 33 : log_base_name = "translog";
172 :
173 : /* tag each filename with a timestamp */
174 49 : timestamp = gnc_date_timestamp ();
175 :
176 49 : filename = g_strconcat (log_base_name.c_str(), ".", timestamp, ".log", nullptr);
177 :
178 49 : trans_log_stream.open(filename, std::ios::app);
179 49 : if (!trans_log_stream.is_open())
180 : {
181 0 : int norr = errno;
182 0 : printf ("Error: xaccOpenLog(): cannot open journal\n"
183 0 : "\t %d %s\n", norr, g_strerror (norr) ? g_strerror (norr) : "");
184 :
185 0 : g_free (filename);
186 0 : g_free (timestamp);
187 0 : return;
188 : }
189 :
190 : /* Save the log file name */
191 49 : auto tmpstr = g_path_get_basename(filename);
192 49 : trans_log_name = tmpstr;
193 49 : g_free (tmpstr);
194 :
195 49 : g_free (filename);
196 49 : g_free (timestamp);
197 :
198 : /* Note: this must match src/import-export/log-replay/gnc-log-replay.c */
199 : trans_log_stream << "mod\ttrans_guid\tsplit_guid\ttime_now\t"
200 : << "date_entered\tdate_posted\t"
201 : << "acc_guid\tacc_name\tnum\tdescription\t"
202 : << "notes\tmemo\taction\treconciled\t"
203 : << "amount\tvalue\tdate_reconciled\n"
204 49 : << "-----------------\n";
205 : }
206 :
207 : /********************************************************************\
208 : \********************************************************************/
209 :
210 : void
211 15 : xaccCloseLog (void)
212 : {
213 15 : if (!trans_log_stream.is_open()) return;
214 15 : trans_log_stream.flush();
215 15 : trans_log_stream.close();
216 : }
217 :
218 : /********************************************************************\
219 : \********************************************************************/
220 :
221 : void
222 9638 : xaccTransWriteLog (Transaction *trans, char flag)
223 : {
224 : GList *node;
225 : char trans_guid_str[GUID_ENCODING_LENGTH + 1];
226 : char split_guid_str[GUID_ENCODING_LENGTH + 1];
227 : const char *trans_notes;
228 : char dnow[100], dent[100], dpost[100], drecn[100];
229 :
230 9638 : if (!gen_logs)
231 : {
232 4862 : PINFO ("Attempt to write disabled transaction log");
233 4862 : return;
234 : }
235 4776 : if (!trans_log_stream.is_open()) return;
236 :
237 4776 : gnc_time64_to_iso8601_buff (gnc_time(nullptr), dnow);
238 4776 : gnc_time64_to_iso8601_buff (trans->date_entered, dent);
239 4776 : gnc_time64_to_iso8601_buff (trans->date_posted, dpost);
240 4776 : guid_to_string_buff (xaccTransGetGUID(trans), trans_guid_str);
241 4776 : trans_notes = xaccTransGetNotes(trans);
242 4776 : trans_log_stream << "===== START\n";
243 :
244 10073 : for (node = trans->splits; node; node = node->next)
245 : {
246 5297 : Split *split = GNC_SPLIT(node->data);
247 5297 : const char * accname = "";
248 : char acc_guid_str[GUID_ENCODING_LENGTH + 1];
249 : gnc_numeric amt, val;
250 :
251 5297 : if (xaccSplitGetAccount(split))
252 : {
253 5266 : accname = xaccAccountGetName (xaccSplitGetAccount(split));
254 5266 : guid_to_string_buff(xaccAccountGetGUID(xaccSplitGetAccount(split)),
255 : acc_guid_str);
256 : }
257 : else
258 : {
259 31 : acc_guid_str[0] = '\0';
260 : }
261 :
262 5297 : gnc_time64_to_iso8601_buff (split->date_reconciled, drecn);
263 :
264 5297 : guid_to_string_buff (xaccSplitGetGUID(split), split_guid_str);
265 5297 : amt = xaccSplitGetAmount (split);
266 5297 : val = xaccSplitGetValue (split);
267 :
268 : trans_log_stream << flag << '\t'
269 : << trans_guid_str << '\t'
270 : << split_guid_str << '\t'
271 : << dnow << '\t'
272 : << dent << '\t'
273 : << dpost << '\t'
274 : << acc_guid_str << '\t'
275 : << (accname ? accname : "") << '\t'
276 5297 : << (trans->num ? trans->num : "") << '\t'
277 5297 : << (trans->description ? trans->description : "") << '\t'
278 : << (trans_notes ? trans_notes : "") << '\t'
279 5297 : << (split->memo ? split->memo : "") << '\t'
280 5297 : << (split->action ? split->action : "") << '\t'
281 5297 : << split->reconciled << '\t'
282 5297 : << gnc_numeric_num(amt) << '/' << gnc_numeric_denom(amt) << '\t'
283 5297 : << gnc_numeric_num(val) << '/' << gnc_numeric_denom(val) << '\t'
284 5297 : << drecn << '\n';
285 : }
286 :
287 4776 : trans_log_stream << "===== END" << std::endl;
288 : }
289 :
290 : /************************ END OF ************************************\
291 : \************************* FILE *************************************/
|