GnuCash c935c2f+
Loading...
Searching...
No Matches
TransLog.cpp
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
43static 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
88static int gen_logs = 1;
89static std::ofstream trans_log_stream;
90static std::string trans_log_name;
91static std::string log_base_name;
92
93/********************************************************************\
94\********************************************************************/
95
96void xaccLogDisable (void)
97{
98 gen_logs = 0;
99}
100void xaccLogEnable (void)
101{
102 gen_logs = 1;
103}
104
105/********************************************************************\
106\********************************************************************/
107
108void
109xaccReopenLog (void)
110{
111 if (trans_log_stream.is_open())
112 {
113 xaccCloseLog();
114 xaccOpenLog();
115 }
116}
117
118
119void
120xaccLogSetBaseName (const char *basepath)
121{
122 if (!basepath) return;
123
124 log_base_name = basepath;
125
126 if (trans_log_stream.is_open())
127 {
128 xaccCloseLog();
129 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 */
140gboolean
141xaccFileIsCurrentLog (const gchar *name)
142{
143 gchar *base;
144
145 if (!name || trans_log_name.empty())
146 return FALSE;
147
148 base = g_path_get_basename(name);
149 bool result = trans_log_name.compare(base) == 0;
150 g_free(base);
151 return result;
152}
153
154/********************************************************************\
155\********************************************************************/
156
157void
158xaccOpenLog (void)
159{
160 char * filename;
161 char * timestamp;
162
163 if (!gen_logs)
164 {
165 PINFO ("Attempt to open disabled transaction log");
166 return;
167 }
168 if (trans_log_stream.is_open()) return;
169
170 if (log_base_name.empty())
171 log_base_name = "translog";
172
173 /* tag each filename with a timestamp */
174 timestamp = gnc_date_timestamp ();
175
176 filename = g_strconcat (log_base_name.c_str(), ".", timestamp, ".log", nullptr);
177
178 trans_log_stream.open(filename, std::ios::app);
179 if (!trans_log_stream.is_open())
180 {
181 int norr = errno;
182 printf ("Error: xaccOpenLog(): cannot open journal\n"
183 "\t %d %s\n", norr, g_strerror (norr) ? g_strerror (norr) : "");
184
185 g_free (filename);
186 g_free (timestamp);
187 return;
188 }
189
190 /* Save the log file name */
191 auto tmpstr = g_path_get_basename(filename);
192 trans_log_name = tmpstr;
193 g_free (tmpstr);
194
195 g_free (filename);
196 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 << "-----------------\n";
205}
206
207/********************************************************************\
208\********************************************************************/
209
210void
211xaccCloseLog (void)
212{
213 if (!trans_log_stream.is_open()) return;
214 trans_log_stream.flush();
215 trans_log_stream.close();
216}
217
218/********************************************************************\
219\********************************************************************/
220
221void
222xaccTransWriteLog (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 if (!gen_logs)
231 {
232 PINFO ("Attempt to write disabled transaction log");
233 return;
234 }
235 if (!trans_log_stream.is_open()) return;
236
237 gnc_time64_to_iso8601_buff (gnc_time(nullptr), dnow);
238 gnc_time64_to_iso8601_buff (trans->date_entered, dent);
239 gnc_time64_to_iso8601_buff (trans->date_posted, dpost);
240 guid_to_string_buff (xaccTransGetGUID(trans), trans_guid_str);
241 trans_notes = xaccTransGetNotes(trans);
242 trans_log_stream << "===== START\n";
243
244 for (node = trans->splits; node; node = node->next)
245 {
246 Split *split = GNC_SPLIT(node->data);
247 const char * accname = "";
248 char acc_guid_str[GUID_ENCODING_LENGTH + 1];
249 gnc_numeric amt, val;
250
251 if (xaccSplitGetAccount(split))
252 {
253 accname = xaccAccountGetName (xaccSplitGetAccount(split));
255 acc_guid_str);
256 }
257 else
258 {
259 acc_guid_str[0] = '\0';
260 }
261
262 gnc_time64_to_iso8601_buff (split->date_reconciled, drecn);
263
264 guid_to_string_buff (xaccSplitGetGUID(split), split_guid_str);
265 amt = xaccSplitGetAmount (split);
266 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 << (trans->num ? trans->num : "") << '\t'
277 << (trans->description ? trans->description : "") << '\t'
278 << (trans_notes ? trans_notes : "") << '\t'
279 << (split->memo ? split->memo : "") << '\t'
280 << (split->action ? split->action : "") << '\t'
281 << split->reconciled << '\t'
282 << gnc_numeric_num(amt) << '/' << gnc_numeric_denom(amt) << '\t'
283 << gnc_numeric_num(val) << '/' << gnc_numeric_denom(val) << '\t'
284 << drecn << '\n';
285 }
286
287 trans_log_stream << "===== END" << std::endl;
288}
289
290/************************ END OF ************************************\
291\************************* FILE *************************************/
Account handling public routines.
API for the transaction logger.
API for Transactions and Splits (journal entries)
const char * xaccAccountGetName(const Account *acc)
Get the account's name.
Definition Account.cpp:3289
#define xaccAccountGetGUID(X)
Definition Account.h:252
char * gnc_time64_to_iso8601_buff(time64 time, char *buff)
The gnc_time64_to_iso8601_buff() routine takes the input UTC time64 value and prints it as an ISO-860...
time64 gnc_time(time64 *tbuf)
get the current time
Definition gnc-date.cpp:262
gchar * gnc_date_timestamp(void)
Make a timestamp in YYYYMMDDHHMMSS format.
#define xaccTransGetGUID(X)
const char * xaccTransGetNotes(const Transaction *trans)
Gets the transaction Notes.
#define GUID_ENCODING_LENGTH
Number of characters needed to encode a guid as a string not including the null terminator.
Definition guid.h:84
gchar * guid_to_string_buff(const GncGUID *guid, gchar *str)
The guid_to_string_buff() routine puts a null-terminated string encoding of the id into the memory po...
Definition guid.cpp:208
#define PINFO(format, args...)
Print an informational note.
Definition qoflog.h:256
void xaccTransWriteLog(Transaction *trans, char flag)
Definition TransLog.cpp:222
void xaccLogSetBaseName(const char *basepath)
The xaccLogSetBaseName() method sets the base filepath and the root part of the journal file name.
Definition TransLog.cpp:120
gboolean xaccFileIsCurrentLog(const gchar *name)
Test a filename to see if it is the name of the current logfile.
Definition TransLog.cpp:141
void xaccLogEnable(void)
document me
Definition TransLog.cpp:100
void xaccLogDisable(void)
document me
Definition TransLog.cpp:96
#define xaccSplitGetGUID(X)
Definition Split.h:579
gnc_numeric xaccSplitGetValue(const Split *split)
Returns the value of this split in the transaction's commodity.
Account * xaccSplitGetAccount(const Split *split)
Returns the account of this split, which was set through xaccAccountInsertSplit().
gnc_numeric xaccSplitGetAmount(const Split *split)
Returns the amount of the split in the account's commodity.