···11+(** Transaction log for debugging unpac operations.
22+33+ Uses Unix I/O so it works both inside and outside Eio context. *)
44+55+let log_file = ".unpac.log"
66+77+let timestamp () =
88+ let t = Unix.gettimeofday () in
99+ let tm = Unix.localtime t in
1010+ let ms = int_of_float ((t -. floor t) *. 1000.) in
1111+ Printf.sprintf "%04d-%02d-%02d %02d:%02d:%02d.%03d"
1212+ (tm.Unix.tm_year + 1900)
1313+ (tm.Unix.tm_mon + 1)
1414+ tm.Unix.tm_mday
1515+ tm.Unix.tm_hour
1616+ tm.Unix.tm_min
1717+ tm.Unix.tm_sec
1818+ ms
1919+2020+let append lines =
2121+ try
2222+ let oc = open_out_gen [Open_append; Open_creat; Open_text] 0o644 log_file in
2323+ List.iter (fun line -> output_string oc (line ^ "\n")) lines;
2424+ close_out oc
2525+ with _ ->
2626+ (* Silently ignore logging failures - don't break the main operation *)
2727+ ()
2828+2929+let separator = "----------------------------------------"
3030+3131+let start_session ~args =
3232+ let ts = timestamp () in
3333+ let cmd = String.concat " " ("unpac" :: args) in
3434+ let cwd = Sys.getcwd () in
3535+ append [
3636+ "";
3737+ separator;
3838+ Printf.sprintf "[%s] SESSION START" ts;
3939+ Printf.sprintf "Command: %s" cmd;
4040+ Printf.sprintf "CWD: %s" cwd;
4141+ separator;
4242+ ]
4343+4444+let end_session ~exit_code =
4545+ let ts = timestamp () in
4646+ let status = if exit_code = 0 then "SUCCESS" else Printf.sprintf "FAILED (exit %d)" exit_code in
4747+ append [
4848+ separator;
4949+ Printf.sprintf "[%s] SESSION END: %s" ts status;
5050+ separator;
5151+ ]
5252+5353+let log_git_command ~args ~exit_code ~stdout ~stderr =
5454+ let ts = timestamp () in
5555+ let cmd = String.concat " " ("git" :: args) in
5656+ let status = if exit_code = 0 then "OK" else Printf.sprintf "FAILED (exit %d)" exit_code in
5757+ let lines = [
5858+ Printf.sprintf "[%s] GIT: %s" ts cmd;
5959+ Printf.sprintf " Status: %s" status;
6060+ ] in
6161+ let lines =
6262+ if String.trim stdout <> "" then
6363+ lines @ [
6464+ " --- stdout ---";
6565+ String.concat "\n" (List.map (fun l -> " " ^ l) (String.split_on_char '\n' (String.trim stdout)));
6666+ ]
6767+ else lines
6868+ in
6969+ let lines =
7070+ if String.trim stderr <> "" then
7171+ lines @ [
7272+ " --- stderr ---";
7373+ String.concat "\n" (List.map (fun l -> " " ^ l) (String.split_on_char '\n' (String.trim stderr)));
7474+ ]
7575+ else lines
7676+ in
7777+ append lines
7878+7979+let log_message msg =
8080+ let ts = timestamp () in
8181+ append [Printf.sprintf "[%s] INFO: %s" ts msg]
8282+8383+let log_error msg =
8484+ let ts = timestamp () in
8585+ append [Printf.sprintf "[%s] ERROR: %s" ts msg]
+31
lib/txn_log.mli
···11+(** Transaction log for debugging unpac operations.
22+33+ Maintains a persistent .unpac.log file with a trace of all operations
44+ and git commands for debugging purposes. Uses Unix I/O so it works
55+ both inside and outside Eio context. *)
66+77+(** {1 Session Management} *)
88+99+val start_session : args:string list -> unit
1010+(** [start_session ~args] logs the start of an unpac session with the
1111+ given command-line arguments. *)
1212+1313+val end_session : exit_code:int -> unit
1414+(** [end_session ~exit_code] logs the end of the current session. *)
1515+1616+(** {1 Command Logging} *)
1717+1818+val log_git_command :
1919+ args:string list ->
2020+ exit_code:int ->
2121+ stdout:string ->
2222+ stderr:string ->
2323+ unit
2424+(** [log_git_command ~args ~exit_code ~stdout ~stderr] logs a git
2525+ command execution with its full output. *)
2626+2727+val log_message : string -> unit
2828+(** [log_message msg] logs an informational message. *)
2929+3030+val log_error : string -> unit
3131+(** [log_error msg] logs an error message. *)