GnuCash  3.7-134-g71bdcdfa6+
TransLog.c
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.h>
32 
33 #include "Account.h"
34 #include "Transaction.h"
35 #include "TransactionP.h"
36 #include "TransLog.h"
37 #include "qof.h"
38 #ifdef _MSC_VER
39 # define g_fopen fopen
40 #endif
41 
42 static QofLogModule log_module = "gnc.translog";
43 
44 /*
45  * Some design philosphy that I think would be good to keep in mind:
46  * (0) Simplicity and foolproofness are the over-riding design points.
47  * This is supposed to be a fail-safe safety net. We don't want
48  * our safety net to fail because of some whiz-bang shenanigans.
49  *
50  * (1) Try to keep the code simple. Want to make it simple and obvious
51  * that we are recording everything that we need to record.
52  *
53  * (2) Keep the printed format human readable, for the same reasons.
54  * (2.a) Keep the format, simple, flat, more or less unstructured,
55  * record oriented. This will help parsing by perl scripts.
56  * No, using a perl script to analyze a file that's supposed to
57  * be human readable is not a contradication in terms -- that's
58  * exactly the point.
59  * (2.b) Use tabs as a human friendly field separator; its also a
60  * character that does not (should not) appear naturally anywhere
61  * in the data, as it serves no formatting purpose in the current
62  * GUI design. (hack alert -- this is not currently tested for
63  * or enforced, so this is a very unsafe assumption. Maybe
64  * urlencoding should be used.)
65  * (2.c) Don't print redundant information in a single record. This
66  * would just confuse any potential user of this file.
67  * (2.d) Saving space, being compact is not a priority, I don't think.
68  *
69  * (3) There are no compatibility requirements from release to release.
70  * Sounds OK to me to change the format of the output when needed.
71  *
72  * (-) print transaction start and end delimiters
73  * (-) print a unique transaction id as a handy label for anyone
74  * who actually examines these logs.
75  * The C address pointer to the transaction struct should be fine,
76  * as it is simple and unique until the transaction is deleted ...
77  * and we log deletions, so that's OK. Just note that the id
78  * for a deleted transaction might be recycled.
79  * (-) print the current timestamp, so that if it is known that a bug
80  * occurred at a certain time, it can be located.
81  * (-) hack alert -- something better than just the account name
82  * is needed for identifying the account.
83  */
84 /* ------------------------------------------------------------------ */
85 
86 
87 static int gen_logs = 1;
88 static FILE * trans_log = NULL;
89 static char * trans_log_name = NULL;
90 static char * log_base_name = NULL;
91 
92 /********************************************************************\
93 \********************************************************************/
94 
95 void xaccLogDisable (void)
96 {
97  gen_logs = 0;
98 }
99 void xaccLogEnable (void)
100 {
101  gen_logs = 1;
102 }
103 
104 /********************************************************************\
105 \********************************************************************/
106 
107 void
108 xaccReopenLog (void)
109 {
110  if (trans_log)
111  {
112  xaccCloseLog();
113  xaccOpenLog();
114  }
115 }
116 
117 
118 void
119 xaccLogSetBaseName (const char *basepath)
120 {
121  if (!basepath) return;
122 
123  g_free (log_base_name);
124  log_base_name = g_strdup (basepath);
125 
126  if (trans_log)
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  gint result;
145 
146  if (!name || !trans_log_name)
147  return FALSE;
148 
149  base = g_path_get_basename(name);
150  result = (strcmp(base, trans_log_name) == 0);
151  g_free(base);
152  return result;
153 }
154 
155 /********************************************************************\
156 \********************************************************************/
157 
158 void
159 xaccOpenLog (void)
160 {
161  char * filename;
162  char * timestamp;
163 
164  if (!gen_logs)
165  {
166  PINFO ("Attempt to open disabled transaction log");
167  return;
168  }
169  if (trans_log) return;
170 
171  if (!log_base_name) log_base_name = g_strdup ("translog");
172 
173  /* tag each filename with a timestamp */
174  timestamp = gnc_date_timestamp ();
175 
176  filename = g_strconcat (log_base_name, ".", timestamp, ".log", NULL);
177 
178  trans_log = g_fopen (filename, "a");
179  if (!trans_log)
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  if (trans_log_name)
192  g_free (trans_log_name);
193  trans_log_name = g_path_get_basename(filename);
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  fprintf (trans_log, "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  fprintf (trans_log, "-----------------\n");
205 }
206 
207 /********************************************************************\
208 \********************************************************************/
209 
210 void
211 xaccCloseLog (void)
212 {
213  if (!trans_log) return;
214  fflush (trans_log);
215  fclose (trans_log);
216  trans_log = NULL;
217 }
218 
219 /********************************************************************\
220 \********************************************************************/
221 
222 void
223 xaccTransWriteLog (Transaction *trans, char flag)
224 {
225  GList *node;
226  char trans_guid_str[GUID_ENCODING_LENGTH + 1];
227  char split_guid_str[GUID_ENCODING_LENGTH + 1];
228  const char *trans_notes;
229  char dnow[100], dent[100], dpost[100], drecn[100];
230 
231  if (!gen_logs)
232  {
233  PINFO ("Attempt to write disabled transaction log");
234  return;
235  }
236  if (!trans_log) return;
237 
238  gnc_time64_to_iso8601_buff (gnc_time(NULL), dnow);
239  gnc_time64_to_iso8601_buff (trans->date_entered, dent);
240  gnc_time64_to_iso8601_buff (trans->date_posted, dpost);
241  guid_to_string_buff (xaccTransGetGUID(trans), trans_guid_str);
242  trans_notes = xaccTransGetNotes(trans);
243  fprintf (trans_log, "===== START\n");
244 
245  for (node = trans->splits; node; node = node->next)
246  {
247  time64 time;
248  Split *split = node->data;
249  const char * accname = "";
250  char acc_guid_str[GUID_ENCODING_LENGTH + 1];
251  gnc_numeric amt, val;
252 
253  if (xaccSplitGetAccount(split))
254  {
255  accname = xaccAccountGetName (xaccSplitGetAccount(split));
257  acc_guid_str);
258  }
259  else
260  {
261  acc_guid_str[0] = '\0';
262  }
263 
264  gnc_time64_to_iso8601_buff (split->date_reconciled, drecn);
265 
266  guid_to_string_buff (xaccSplitGetGUID(split), split_guid_str);
267  amt = xaccSplitGetAmount (split);
268  val = xaccSplitGetValue (split);
269 
270  /* use tab-separated fields */
271  fprintf (trans_log,
272  "%c\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t"
273  "%s\t%s\t%s\t%s\t%c\t%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT "\t%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT "\t%s\n",
274  flag,
275  trans_guid_str, split_guid_str, /* trans+split make up unique id */
276  /* Note that the next three strings always exist,
277  * so we don't need to test them. */
278  dnow,
279  dent,
280  dpost,
281  acc_guid_str,
282  accname ? accname : "",
283  trans->num ? trans->num : "",
284  trans->description ? trans->description : "",
285  trans_notes ? trans_notes : "",
286  split->memo ? split->memo : "",
287  split->action ? split->action : "",
288  split->reconciled,
289  gnc_numeric_num(amt),
290  gnc_numeric_denom(amt),
291  gnc_numeric_num(val),
292  gnc_numeric_denom(val),
293  /* The next string always exists. No need to test it. */
294  drecn);
295  }
296 
297  fprintf (trans_log, "===== END\n");
298 
299  /* get data out to the disk */
300  fflush (trans_log);
301 }
302 
303 /************************ END OF ************************************\
304 \************************* FILE *************************************/
char * gnc_date_timestamp(void)
Make a timestamp in YYYYMMDDHHMMSS format.
Definition: gnc-date.cpp:1103
#define PINFO(format, args...)
Print an informational note.
Definition: qoflog.h:256
void xaccTransWriteLog(Transaction *trans, char flag)
Definition: TransLog.c:223
void xaccLogDisable(void)
document me
Definition: TransLog.c:95
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:174
const char * xaccTransGetNotes(const Transaction *trans)
Gets the transaction Notes.
Definition: Transaction.c:2349
#define xaccAccountGetGUID(X)
Definition: Account.h:248
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:544
#define xaccTransGetGUID(X)
Definition: Transaction.h:773
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.c:119
gnc_numeric xaccSplitGetValue(const Split *split)
Returns the value of this split in the transaction&#39;s commodity.
Definition: Split.c:1890
Account * xaccSplitGetAccount(const Split *s)
Returns the account of this split, which was set through xaccAccountInsertSplit().
Definition: Split.c:912
gboolean xaccFileIsCurrentLog(const gchar *name)
Test a filename to see if it is the name of the current logfile.
Definition: TransLog.c:141
time64 gnc_time(time64 *tbuf)
get the current local time
Definition: gnc-date.cpp:273
gint64 time64
Many systems, including Microsoft Windows and BSD-derived Unixes like Darwin, are retaining the int-3...
Definition: gnc-date.h:84
const char * xaccAccountGetName(const Account *acc)
Get the account&#39;s name.
Definition: Account.cpp:3028
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:1142
void xaccLogEnable(void)
document me
Definition: TransLog.c:99
const gchar * QofLogModule
QofLogModule declaration.
Definition: qofid.h:89
gnc_numeric xaccSplitGetAmount(const Split *split)
Returns the amount of the split in the account&#39;s commodity.
Definition: Split.c:1884