LCOV - code coverage report
Current view: top level - libgnucash/engine - TransLog.cpp (source / functions) Coverage Total Hit
Test: gnucash.info Lines: 78.0 % 82 64
Test Date: 2026-11-02 18:19:43 Functions: 75.0 % 8 6
Legend: Lines:     hit not hit

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

Generated by: LCOV version 2.0-1