GnuCash  2.6.99
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
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 [email protected] *
21  * *
22 \********************************************************************/
23 
24 #include "config.h"
25 
26 #include <errno.h>
27 #include <glib.h>
28 #include <glib/gstdio.h>
29 #include <string.h>
30 
31 #include "Account.h"
32 #include "Transaction.h"
33 #include "TransactionP.h"
34 #include "TransLog.h"
35 #include "qof.h"
36 #ifdef _MSC_VER
37 # define g_fopen fopen
38 #endif
39 
40 static QofLogModule log_module = "gnc.translog";
41 
42 /*
43  * Some design philosphy that I think would be good to keep in mind:
44  * (0) Simplicity and foolproofness are the over-riding design points.
45  * This is supposed to be a fail-safe safety net. We don't want
46  * our safety net to fail because of some whiz-bang shenanigans.
47  *
48  * (1) Try to keep the code simple. Want to make it simple and obvious
49  * that we are recording everything that we need to record.
50  *
51  * (2) Keep the printed format human readable, for the same reasons.
52  * (2.a) Keep the format, simple, flat, more or less unstructured,
53  * record oriented. This will help parsing by perl scripts.
54  * No, using a perl script to analyze a file that's supposed to
55  * be human readable is not a contradication in terms -- that's
56  * exactly the point.
57  * (2.b) Use tabs as a human friendly field separator; its also a
58  * character that does not (should not) appear naturally anywhere
59  * in the data, as it serves no formatting purpose in the current
60  * GUI design. (hack alert -- this is not currently tested for
61  * or enforced, so this is a very unsafe assumption. Maybe
62  * urlencoding should be used.)
63  * (2.c) Don't print redundant information in a single record. This
64  * would just confuse any potential user of this file.
65  * (2.d) Saving space, being compact is not a priority, I don't think.
66  *
67  * (3) There are no compatibility requirements from release to release.
68  * Sounds OK to me to change the format of the output when needed.
69  *
70  * (-) print transaction start and end delimiters
71  * (-) print a unique transaction id as a handy label for anyone
72  * who actually examines these logs.
73  * The C address pointer to the transaction struct should be fine,
74  * as it is simple and unique until the transaction is deleted ...
75  * and we log deletions, so that's OK. Just note that the id
76  * for a deleted transaction might be recycled.
77  * (-) print the current timestamp, so that if it is known that a bug
78  * occurred at a certain time, it can be located.
79  * (-) hack alert -- something better than just the account name
80  * is needed for identifying the account.
81  */
82 /* ------------------------------------------------------------------ */
83 
84 
85 static int gen_logs = 1;
86 static FILE * trans_log = NULL;
87 static char * trans_log_name = NULL;
88 static char * log_base_name = NULL;
89 
90 /********************************************************************\
91 \********************************************************************/
92 
93 void xaccLogDisable (void)
94 {
95  gen_logs = 0;
96 }
97 void xaccLogEnable (void)
98 {
99  gen_logs = 1;
100 }
101 
102 /********************************************************************\
103 \********************************************************************/
104 
105 void
106 xaccReopenLog (void)
107 {
108  if (trans_log)
109  {
110  xaccCloseLog();
111  xaccOpenLog();
112  }
113 }
114 
115 
116 void
117 xaccLogSetBaseName (const char *basepath)
118 {
119  if (!basepath) return;
120 
121  g_free (log_base_name);
122  log_base_name = g_strdup (basepath);
123 
124  if (trans_log)
125  {
126  xaccCloseLog();
127  xaccOpenLog();
128  }
129 }
130 
131 
132 /*
133  * See if the provided file name is that of the current log file.
134  * Since the filename is generated with a time-stamp we can ignore the
135  * directory path and avoid problems with worrying about any ".."
136  * components in the path.
137  */
138 gboolean
139 xaccFileIsCurrentLog (const gchar *name)
140 {
141  gchar *base;
142  gint result;
143 
144  if (!name || !trans_log_name)
145  return FALSE;
146 
147  base = g_path_get_basename(name);
148  result = (strcmp(base, trans_log_name) == 0);
149  g_free(base);
150  return result;
151 }
152 
153 /********************************************************************\
154 \********************************************************************/
155 
156 void
157 xaccOpenLog (void)
158 {
159  char * filename;
160  char * timestamp;
161 
162  if (!gen_logs)
163  {
164  PINFO ("Attempt to open disabled transaction log");
165  return;
166  }
167  if (trans_log) return;
168 
169  if (!log_base_name) log_base_name = g_strdup ("translog");
170 
171  /* tag each filename with a timestamp */
172  timestamp = gnc_date_timestamp ();
173 
174  filename = g_strconcat (log_base_name, ".", timestamp, ".log", NULL);
175 
176  trans_log = g_fopen (filename, "a");
177  if (!trans_log)
178  {
179  int norr = errno;
180  printf ("Error: xaccOpenLog(): cannot open journal \n"
181  "\t %d %s\n", norr, g_strerror (norr) ? g_strerror (norr) : "");
182 
183  g_free (filename);
184  g_free (timestamp);
185  return;
186  }
187 
188  /* Save the log file name */
189  if (trans_log_name)
190  g_free (trans_log_name);
191  trans_log_name = g_path_get_basename(filename);
192 
193  g_free (filename);
194  g_free (timestamp);
195 
196  /* Note: this must match src/import-export/log-replay/gnc-log-replay.c */
197  fprintf (trans_log, "mod\ttrans_guid\tsplit_guid\ttime_now\t"
198  "date_entered\tdate_posted\t"
199  "acc_guid\tacc_name\tnum\tdescription\t"
200  "notes\tmemo\taction\treconciled\t"
201  "amount\tvalue\tdate_reconciled\n");
202  fprintf (trans_log, "-----------------\n");
203 }
204 
205 /********************************************************************\
206 \********************************************************************/
207 
208 void
209 xaccCloseLog (void)
210 {
211  if (!trans_log) return;
212  fflush (trans_log);
213  fclose (trans_log);
214  trans_log = NULL;
215 }
216 
217 /********************************************************************\
218 \********************************************************************/
219 
220 void
221 xaccTransWriteLog (Transaction *trans, char flag)
222 {
223  GList *node;
224  char trans_guid_str[GUID_ENCODING_LENGTH + 1];
225  char split_guid_str[GUID_ENCODING_LENGTH + 1];
226  const char *trans_notes;
227  char dnow[100], dent[100], dpost[100], drecn[100];
228  Timespec ts;
229 
230  if (!gen_logs)
231  {
232  PINFO ("Attempt to write disabled transaction log");
233  return;
234  }
235  if (!trans_log) return;
236 
237  timespecFromTime64(&ts, gnc_time (NULL));
238  gnc_timespec_to_iso8601_buff (ts, dnow);
239 
240  timespecFromTime64(&ts, trans->date_entered.tv_sec);
241  gnc_timespec_to_iso8601_buff (ts, dent);
242 
243  timespecFromTime64(&ts, trans->date_posted.tv_sec);
244  gnc_timespec_to_iso8601_buff (ts, dpost);
245 
246  guid_to_string_buff (xaccTransGetGUID(trans), trans_guid_str);
247  trans_notes = xaccTransGetNotes(trans);
248  fprintf (trans_log, "===== START\n");
249 
250  for (node = trans->splits; node; node = node->next)
251  {
252  Split *split = node->data;
253  const char * accname = "";
254  char acc_guid_str[GUID_ENCODING_LENGTH + 1];
255  gnc_numeric amt, val;
256 
257  if (xaccSplitGetAccount(split))
258  {
259  accname = xaccAccountGetName (xaccSplitGetAccount(split));
261  acc_guid_str);
262  }
263  else
264  {
265  acc_guid_str[0] = '\0';
266  }
267 
268  timespecFromTime64(&ts, split->date_reconciled.tv_sec);
269  gnc_timespec_to_iso8601_buff (ts, drecn);
270 
271  guid_to_string_buff (xaccSplitGetGUID(split), split_guid_str);
272  amt = xaccSplitGetAmount (split);
273  val = xaccSplitGetValue (split);
274 
275  /* use tab-separated fields */
276  fprintf (trans_log,
277  "%c\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t"
278  "%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",
279  flag,
280  trans_guid_str, split_guid_str, /* trans+split make up unique id */
281  /* Note that the next three strings always exist,
282  * so we don't need to test them. */
283  dnow,
284  dent,
285  dpost,
286  acc_guid_str,
287  accname ? accname : "",
288  trans->num ? trans->num : "",
289  trans->description ? trans->description : "",
290  trans_notes ? trans_notes : "",
291  split->memo ? split->memo : "",
292  split->action ? split->action : "",
293  split->reconciled,
294  gnc_numeric_num(amt),
295  gnc_numeric_denom(amt),
296  gnc_numeric_num(val),
297  gnc_numeric_denom(val),
298  /* The next string always exists. No need to test it. */
299  drecn);
300  }
301 
302  fprintf (trans_log, "===== END\n");
303 
304  /* get data out to the disk */
305  fflush (trans_log);
306 }
307 
308 /************************ END OF ************************************\
309 \************************* FILE *************************************/
gchar * gnc_timespec_to_iso8601_buff(Timespec ts, gchar *buff)
#define PINFO(format, args...)
Definition: qoflog.h:249
void xaccTransWriteLog(Transaction *trans, char flag)
Definition: TransLog.c:221
void xaccLogDisable(void)
Definition: TransLog.c:93
Use a 64-bit unsigned int timespec.
Definition: gnc-date.h:299
gchar * guid_to_string_buff(const GncGUID *guid, gchar *buff)
const char * xaccTransGetNotes(const Transaction *trans)
Definition: Transaction.c:2197
#define xaccAccountGetGUID(X)
Definition: Account.h:239
Account handling public routines.
#define GUID_ENCODING_LENGTH
Definition: guid.h:74
#define xaccSplitGetGUID(X)
Definition: Split.h:521
#define xaccTransGetGUID(X)
Definition: Transaction.h:755
API for the transaction logger.
void xaccLogSetBaseName(const char *basepath)
Definition: TransLog.c:117
Definition: SplitP.h:71
gnc_numeric xaccSplitGetValue(const Split *split)
Definition: Split.c:1993
Account * xaccSplitGetAccount(const Split *s)
Definition: Split.c:968
gboolean xaccFileIsCurrentLog(const gchar *name)
Definition: TransLog.c:139
time64 gnc_time(time64 *tbuf)
get the current local time
const char * xaccAccountGetName(const Account *acc)
Definition: Account.c:3031
API for Transactions and Splits (journal entries)
void xaccLogEnable(void)
Definition: TransLog.c:97
const gchar * QofLogModule
Definition: qofid.h:89
gnc_numeric xaccSplitGetAmount(const Split *split)
Definition: Split.c:1987
void timespecFromTime64(Timespec *ts, time64 t)
char * gnc_date_timestamp(void)
Make a timestamp in YYYYMMDDHHMMSS format.