···11(** A version of the logging module that adds timestamps to the logs *)
22+23module Log = struct
34 let timestamp_tag =
45 let now () = Unix.gettimeofday () |> Unix.localtime in
···3132 let app ?src fn = Logs.app ?src (timestamp_wrap fn)
3233end
33343434-module Internal =struct
3535-let reporter ppf =
3636- let report src level ~over k msgf =
3737- let k _ = over (); k () in
3838- let with_stamp h tags k ppf fmt =
3939- let stamp = match tags with
4040- | None -> None
4141- | Some tags -> Logs.Tag.find Log.timestamp_tag tags
3535+module Internal = struct
3636+ let reporter ppf =
3737+ let report src level ~over k msgf =
3838+ let k _ =
3939+ over ();
4040+ k ()
4141+ in
4242+ let with_stamp h tags k ppf fmt =
4343+ let stamp =
4444+ match tags with
4545+ | None ->
4646+ None
4747+ | Some tags ->
4848+ Logs.Tag.find Log.timestamp_tag tags
4949+ in
5050+ let dt = Format.pp_print_option (Logs.Tag.printer Log.timestamp_tag) in
5151+ Format.kfprintf
5252+ k
5353+ ppf
5454+ ("%a[%a] @[" ^^ fmt ^^ "@]@.")
5555+ Logs.pp_header
5656+ (level, h)
5757+ dt
5858+ stamp
4259 in
4343- let dt = Format.pp_print_option (Logs.Tag.printer Log.timestamp_tag) in
4444- Format.kfprintf k ppf ("%a[%a] @["^^fmt^^"@]@.")
4545- Logs.pp_header (level, h) dt stamp
6060+ msgf @@ fun ?header ?tags fmt -> with_stamp header tags k ppf fmt
4661 in
4747- msgf @@ fun ?header ?tags fmt -> with_stamp header tags k ppf fmt
4848- in
4949- { Logs.report = report }
5050-(** Removes old log files, keeping only the 20 most recent *)
5151-let cleanup_logs () =
5252- let state_home = Unix.getenv "XDG_STATE_HOME" in
5353- let jj_tui_dir = Filename.concat state_home "jj_tui" in
5454- let log_files = Sys.readdir jj_tui_dir
5555- |> Array.to_list
5656- |> List.filter (fun file -> Filename.check_suffix file ".log")
5757- |> List.sort (fun a b -> String.compare b a) in
5858- if List.length log_files > 20 then
5959- List.iteri (fun i file ->
6060- if i >= 20 then
6161- let file_path = Filename.concat jj_tui_dir file in
6262- Unix.unlink file_path
6363- ) log_files
6464-;;
6565-let init_logging () =
6666- (*creates or opens the log file*)
6767- let get_log_file_channel () =
6262+ { Logs.report }
6363+ ;;
6464+6565+ (** Removes old log files, keeping only the 20 most recent *)
6666+ let cleanup_logs () =
6867 let state_home = Unix.getenv "XDG_STATE_HOME" in
6968 let jj_tui_dir = Filename.concat state_home "jj_tui" in
7070- (try Unix.mkdir jj_tui_dir 0o755 with Unix.Unix_error (Unix.EEXIST, _, _) -> ());
7171- let timestamp = Unix.time () |> Unix.localtime in
7272- let timestamp_str = Printf.sprintf "%04d%02d%02d_%02d%02d%02d"
7373- (timestamp.tm_year + 1900) (timestamp.tm_mon + 1) timestamp.tm_mday
7474- timestamp.tm_hour timestamp.tm_min timestamp.tm_sec in
7575- let log_file = Filename.concat jj_tui_dir (Printf.sprintf "log_%s.log" timestamp_str) in
7676- let log_channel = open_out_gen [ Open_append; Open_creat ] 0o644 log_file in
7777- log_channel
7878- in
7979- (*Make a mutex for logging to prevent concurrency and threading issues *)
8080- let logging_mutex = Picos_std_sync.Mutex.create () in
8181- Logs.set_reporter_mutex
8282- ~lock:(fun () -> Picos_std_sync.Mutex.lock logging_mutex)
8383- ~unlock:(fun () -> Picos_std_sync.Mutex.unlock logging_mutex);
8484- (* log our logs into the log file*)
8585- let log_chan = get_log_file_channel () in
8686- let log_formatter = Format.formatter_of_out_channel log_chan in
8787- let reporter = reporter log_formatter in
8888- Logs.set_level (Some Debug);
8989- Logs.set_reporter reporter;
9090- (*make sure everything is working*)
9191- [%log info "Logging initialized"];
9292- cleanup_logs ();
9393- [%log debug "Old logs cleaned up"]
9494-;;
6969+ let log_files =
7070+ Sys.readdir jj_tui_dir
7171+ |> Array.to_list
7272+ |> List.filter (fun file -> Filename.check_suffix file ".log")
7373+ |> List.sort (fun a b -> String.compare b a)
7474+ in
7575+ if List.length log_files > 20
7676+ then
7777+ List.iteri
7878+ (fun i file ->
7979+ if i >= 20
8080+ then (
8181+ let file_path = Filename.concat jj_tui_dir file in
8282+ Unix.unlink file_path))
8383+ log_files
8484+ ;;
8585+8686+ let normalise_os raw =
8787+ match String.lowercase_ascii raw with "darwin" | "osx" -> "macos" | s -> s
8888+ ;;
8989+9090+ let poll_os () =
9191+ let raw =
9292+ match Sys.os_type with
9393+ | "Unix" ->
9494+ (try
9595+ let uname_in, _ = Unix.open_process_args "uname" [| "uname"; "-s" |] in
9696+ let str = uname_in |> In_channel.input_all in
9797+ Some (str |> String.lowercase_ascii |> String.trim)
9898+ with
9999+ | _ ->
100100+ None)
101101+ | s ->
102102+ Some (s |> String.lowercase_ascii |> String.trim)
103103+ in
104104+ match raw with None | Some "" -> None | Some s -> Some (normalise_os s)
105105+ ;;
106106+107107+ (*tries to get the logging dir for macos and linux*)
108108+ let get_log_dir () =
109109+ let os = poll_os () in
110110+ let state_home =
111111+ try
112112+ match os with
113113+ | Some "linux" ->
114114+ Some (Unix.getenv "XDG_STATE_HOME")
115115+ | Some "macos" ->
116116+ Some "~/Library/Logs"
117117+ | Some "windows" ->
118118+ Some "~/AppData/Local"
119119+ | _ ->
120120+ None
121121+ with
122122+ | _ ->
123123+ None
124124+ in
125125+ let state_home =
126126+ Option.bind state_home (fun x -> if Sys.is_directory x then Some x else None)
127127+ in
128128+ match state_home with
129129+ | None ->
130130+ (try
131131+ Unix.mkdir "~/.jj_tui" 0o755;
132132+ Some "~/.jj_tui"
133133+ with
134134+ | Unix.Unix_error (Unix.EEXIST, _, _) ->
135135+ None)
136136+ | a ->
137137+ a
138138+ ;;
95139140140+ let init_logging () =
141141+ (*creates or opens the log file*)
142142+ let get_log_file_channel () =
143143+ get_log_dir ()
144144+ |> Option.map @@ fun state_home ->
145145+ let jj_tui_dir = Filename.concat state_home "jj_tui" in
146146+ (try Unix.mkdir jj_tui_dir 0o755 with Unix.Unix_error (Unix.EEXIST, _, _) -> ());
147147+ let timestamp = Unix.time () |> Unix.localtime in
148148+ let timestamp_str =
149149+ Printf.sprintf
150150+ "%04d%02d%02d_%02d%02d%02d"
151151+ (timestamp.tm_year + 1900)
152152+ (timestamp.tm_mon + 1)
153153+ timestamp.tm_mday
154154+ timestamp.tm_hour
155155+ timestamp.tm_min
156156+ timestamp.tm_sec
157157+ in
158158+ let log_file =
159159+ Filename.concat jj_tui_dir (Printf.sprintf "log_%s.log" timestamp_str)
160160+ in
161161+ let log_channel = open_out_gen [ Open_append; Open_creat ] 0o644 log_file in
162162+ log_channel
163163+ in
164164+ (* log our logs into the log file*)
165165+ let log_chan = get_log_file_channel () in
166166+ match log_chan with
167167+ | Some log_chan ->
168168+ (*Make a mutex for logging to prevent concurrency and threading issues *)
169169+ let logging_mutex = Picos_std_sync.Mutex.create () in
170170+ Logs.set_reporter_mutex
171171+ ~lock:(fun () -> Picos_std_sync.Mutex.lock logging_mutex)
172172+ ~unlock:(fun () -> Picos_std_sync.Mutex.unlock logging_mutex);
173173+ let log_formatter = Format.formatter_of_out_channel log_chan in
174174+ let reporter = reporter log_formatter in
175175+ Logs.set_level (Some Debug);
176176+ Logs.set_reporter reporter;
177177+ (*make sure everything is working*)
178178+ [%log info "Logging initialized"];
179179+ cleanup_logs ();
180180+ [%log debug "Old logs cleaned up"]
181181+ | None ->
182182+ ()
183183+ ;;
96184end
9797-(**
9898-Initialize the logging system
9999-*)
100100-let init_logging= Internal.init_logging
185185+186186+(** Initialize the logging system *)
187187+let init_logging = Internal.init_logging