Repositories / gitweb2.git

gitweb2.git

Clone (read-only): git clone http://git.guha-anderson.com/git/gitweb2.git

Branch

Add HTTP access logging via logs; ignore _build

Wire Cohttp_eio.src and a gitweb2.access source with Logs_fmt and
Logs_threaded. Log each request with client, method, path, status, bytes,
and latency. Declare logs and eio dependencies. Add _build/ to .gitignore.

Made-with: Cursor
Author
Arjun Guha <a.guha@northeastern.edu>
Date
2026-04-30 05:32:11 -0400
Commit
92ca174378cdbcfd7fc7ae82c70a7dddd117f19b
.gitignore
index d0267f7..ade5402 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
 .build/
+_build/
 build/
 .gradle/
 *.kexe
dune-project
index b78cf77..a8842f5 100644
--- a/dune-project
+++ b/dune-project
@@ -11,6 +11,8 @@
   ppx_format
   ocaml-git
   cohttp-eio
+  eio
   eio_main
+  logs
   uri
   alcotest))
gitweb2.opam
index 94fc82d..6ff9723 100644
--- a/gitweb2.opam
+++ b/gitweb2.opam
@@ -7,7 +7,9 @@ depends: [
   "ppx_format"
   "ocaml-git"
   "cohttp-eio"
+  "eio"
   "eio_main"
+  "logs"
   "uri"
   "alcotest"
   "odoc" {with-doc}
src/dune
index c115b2f..cb401c7 100644
--- a/src/dune
+++ b/src/dune
@@ -2,7 +2,7 @@
  (name gitweb2)
  (public_name gitweb2)
  (modules gitweb2)
- (libraries ocaml-git unix uri cohttp-eio eio_main)
+ (libraries ocaml-git unix uri cohttp-eio eio eio_main logs logs.fmt logs.threaded)
  (preprocess
   (pps ppx_format)))
 
src/gitweb2.ml
index e6b768b..6df0027 100644
--- a/src/gitweb2.ml
+++ b/src/gitweb2.ml
@@ -27,6 +27,9 @@ let default_port : int = 8080
 let default_pygments_command : string = "pygmentize"
 let max_scan_depth : int = 5
 
+let access_src =
+  Logs.Src.create "gitweb2.access" ~doc:"HTTP request access log (method, path, status, bytes)"
+
 let html (value : string) : string =
   let buffer = Buffer.create (String.length value) in
   String.iter
@@ -358,6 +361,10 @@ let route ?(pygments_command : string = default_pygments_command) ~(root : strin
   | exn -> { status = 500; body = page "Server error" ("<p class=\"notice\">" ^ html (Printexc.to_string exn) ^ "</p>") }
 
 let start (config : config) : unit =
+  Logs.set_reporter (Logs_fmt.reporter ());
+  Logs_threaded.enable ();
+  Logs.Src.set_level Cohttp_eio.src (Some Logs.Info);
+  Logs.Src.set_level access_src (Some Logs.Info);
   Eio_main.run @@ fun env ->
   Eio.Switch.run @@ fun sw ->
   let net = Eio.Stdenv.net env in
@@ -374,14 +381,24 @@ let start (config : config) : unit =
   Printf.printf "gitweb2 serving repositories from %s at http://%s:%d/\n%!" config.root config.host config.port;
   let server =
     Cohttp_eio.Server.make
-      ~callback:(fun _conn request _body ->
-        let path = Cohttp.Request.resource request in
-        let response = route ~pygments_command:config.pygments_command ~root:config.root path in
+      ~callback:(fun conn request _body ->
+        let client = snd (fst conn) in
+        let started = Unix.gettimeofday () in
+        let resource = Cohttp.Request.resource request in
+        let meth = Cohttp.Request.meth request in
+        let version = Cohttp.Request.version request in
+        let response = route ~pygments_command:config.pygments_command ~root:config.root resource in
+        let elapsed_ms = (Unix.gettimeofday () -. started) *. 1000. in
+        let size = String.length response.body in
+        Logs.info ~src:access_src (fun m ->
+            m "%a \"%s %s %s\" %d %d %.1fms" Eio.Net.Sockaddr.pp client (Cohttp.Code.string_of_method meth)
+              resource (Cohttp.Code.string_of_version version) response.status size elapsed_ms);
         let headers = Cohttp.Header.init_with "Cache-Control" "no-store" |> fun h -> Cohttp.Header.add h "Content-Type" "text/html; charset=utf-8" in
         Cohttp_eio.Server.respond_string ~headers ~status:(`Code response.status) ~body:response.body ())
       ()
   in
-  Cohttp_eio.Server.run socket server ~on_error:(fun exn -> prerr_endline (Printexc.to_string exn))
+  Cohttp_eio.Server.run socket server
+    ~on_error:(fun exn -> Logs.err (fun m -> m "%s" (Printexc.to_string exn)))
 
 let usage : string = "usage: gitweb2 <repo-root> [--host 127.0.0.1] [--port 8080] [--pygments pygmentize]"