Branch data 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 : 144 : void xaccLogDisable (void)
97 : : {
98 : 144 : gen_logs = 0;
99 : 144 : }
100 : 130 : void xaccLogEnable (void)
101 : : {
102 : 130 : gen_logs = 1;
103 : 130 : }
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 : 47 : xaccLogSetBaseName (const char *basepath)
121 : : {
122 : 47 : if (!basepath) return;
123 : :
124 : 38 : log_base_name = basepath;
125 : :
126 : 38 : if (trans_log_stream.is_open())
127 : : {
128 : 10 : xaccCloseLog();
129 : 10 : 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 : 4815 : xaccOpenLog (void)
159 : : {
160 : : char * filename;
161 : : char * timestamp;
162 : :
163 : 4815 : if (!gen_logs)
164 : : {
165 : 2455 : PINFO ("Attempt to open disabled transaction log");
166 : 2455 : return;
167 : : }
168 : 2360 : if (trans_log_stream.is_open()) return;
169 : :
170 : 43 : if (log_base_name.empty())
171 : 32 : log_base_name = "translog";
172 : :
173 : : /* tag each filename with a timestamp */
174 : 43 : timestamp = gnc_date_timestamp ();
175 : :
176 : 43 : filename = g_strconcat (log_base_name.c_str(), ".", timestamp, ".log", nullptr);
177 : :
178 : 43 : trans_log_stream.open(filename, std::ios::app);
179 : 43 : 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 : 43 : auto tmpstr = g_path_get_basename(filename);
192 : 43 : trans_log_name = tmpstr;
193 : 43 : g_free (tmpstr);
194 : :
195 : 43 : g_free (filename);
196 : 43 : 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 : 43 : << "-----------------\n";
205 : : }
206 : :
207 : : /********************************************************************\
208 : : \********************************************************************/
209 : :
210 : : void
211 : 10 : xaccCloseLog (void)
212 : : {
213 : 10 : if (!trans_log_stream.is_open()) return;
214 : 10 : trans_log_stream.flush();
215 : 10 : trans_log_stream.close();
216 : : }
217 : :
218 : : /********************************************************************\
219 : : \********************************************************************/
220 : :
221 : : void
222 : 9506 : 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 : 9506 : if (!gen_logs)
231 : : {
232 : 4806 : PINFO ("Attempt to write disabled transaction log");
233 : 4806 : return;
234 : : }
235 : 4700 : if (!trans_log_stream.is_open()) return;
236 : :
237 : 4700 : gnc_time64_to_iso8601_buff (gnc_time(nullptr), dnow);
238 : 4700 : gnc_time64_to_iso8601_buff (trans->date_entered, dent);
239 : 4700 : gnc_time64_to_iso8601_buff (trans->date_posted, dpost);
240 : 4700 : guid_to_string_buff (xaccTransGetGUID(trans), trans_guid_str);
241 : 4700 : trans_notes = xaccTransGetNotes(trans);
242 : 4700 : trans_log_stream << "===== START\n";
243 : :
244 : 9882 : for (node = trans->splits; node; node = node->next)
245 : : {
246 : 5182 : Split *split = GNC_SPLIT(node->data);
247 : 5182 : const char * accname = "";
248 : : char acc_guid_str[GUID_ENCODING_LENGTH + 1];
249 : : gnc_numeric amt, val;
250 : :
251 : 5182 : if (xaccSplitGetAccount(split))
252 : : {
253 : 5151 : accname = xaccAccountGetName (xaccSplitGetAccount(split));
254 : 5151 : 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 : 5182 : gnc_time64_to_iso8601_buff (split->date_reconciled, drecn);
263 : :
264 : 5182 : guid_to_string_buff (xaccSplitGetGUID(split), split_guid_str);
265 : 5182 : amt = xaccSplitGetAmount (split);
266 : 5182 : 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 : 5182 : << (trans->num ? trans->num : "") << '\t'
277 : 5182 : << (trans->description ? trans->description : "") << '\t'
278 : : << (trans_notes ? trans_notes : "") << '\t'
279 : 5182 : << (split->memo ? split->memo : "") << '\t'
280 : 5182 : << (split->action ? split->action : "") << '\t'
281 : 5182 : << split->reconciled << '\t'
282 : 5182 : << gnc_numeric_num(amt) << '/' << gnc_numeric_denom(amt) << '\t'
283 : 5182 : << gnc_numeric_num(val) << '/' << gnc_numeric_denom(val) << '\t'
284 : 5182 : << drecn << '\n';
285 : : }
286 : :
287 : 4700 : trans_log_stream << "===== END" << std::endl;
288 : : }
289 : :
290 : : /************************ END OF ************************************\
291 : : \************************* FILE *************************************/
|