GnuCash  5.6-150-g038405b370+
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 
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;
90 static std::string trans_log_name;
91 static std::string log_base_name;
92 
93 /********************************************************************\
94 \********************************************************************/
95 
96 void xaccLogDisable (void)
97 {
98  gen_logs = 0;
99 }
100 void xaccLogEnable (void)
101 {
102  gen_logs = 1;
103 }
104 
105 /********************************************************************\
106 \********************************************************************/
107 
108 void
109 xaccReopenLog (void)
110 {
111  if (trans_log_stream.is_open())
112  {
113  xaccCloseLog();
114  xaccOpenLog();
115  }
116 }
117 
118 
119 void
120 xaccLogSetBaseName (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  */
140 gboolean
141 xaccFileIsCurrentLog (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 
157 void
158 xaccOpenLog (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 
210 void
211 xaccCloseLog (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 
221 void
222 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  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 *************************************/
char * gnc_date_timestamp(void)
Make a timestamp in YYYYMMDDHHMMSS format.
Definition: gnc-date.cpp:1107
#define PINFO(format, args...)
Print an informational note.
Definition: qoflog.h:256
void xaccTransWriteLog(Transaction *trans, char flag)
Definition: TransLog.cpp:222
void xaccLogDisable(void)
document me
Definition: TransLog.cpp:96
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:173
const char * xaccTransGetNotes(const Transaction *trans)
Gets the transaction Notes.
#define xaccAccountGetGUID(X)
Definition: Account.h:252
Account handling public routines.
#define GUID_ENCODING_LENGTH
Number of characters needed to encode a guid as a string not including the null terminator.
Definition: guid.h:84
#define xaccSplitGetGUID(X)
Definition: Split.h:552
#define xaccTransGetGUID(X)
Definition: Transaction.h:788
API for the transaction logger.
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
gnc_numeric xaccSplitGetValue(const Split *split)
Returns the value of this split in the transaction&#39;s commodity.
Definition: gmock-Split.cpp:84
Account * xaccSplitGetAccount(const Split *split)
Returns the account of this split, which was set through xaccAccountInsertSplit().
Definition: gmock-Split.cpp:53
gboolean xaccFileIsCurrentLog(const gchar *name)
Test a filename to see if it is the name of the current logfile.
Definition: TransLog.cpp:141
time64 gnc_time(time64 *tbuf)
get the current time
Definition: gnc-date.cpp:260
const char * xaccAccountGetName(const Account *acc)
Get the account&#39;s name.
Definition: Account.cpp:3255
API for Transactions and Splits (journal entries)
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...
Definition: gnc-date.cpp:1146
void xaccLogEnable(void)
document me
Definition: TransLog.cpp:100
gnc_numeric xaccSplitGetAmount(const Split *split)
Returns the amount of the split in the account&#39;s commodity.
Definition: gmock-Split.cpp:69