Printing to STDERR and STDOUT in OCaml
In OCaml, the standard library provides straightforward functions for writing to standard output and standard error.
Printing to STDOUT
Use print_endline to write a string followed by a newline to standard output:
print_endline "hello world!"
This function is in the Stdlib module (formerly Pervasives in older OCaml versions) and automatically flushes the output buffer. The signature is:
val print_endline : string -> unit
For printing without a newline:
print_string "hello "
Printing to STDERR
Use prerr_endline and prerr_string for standard error:
prerr_endline "error: something went wrong"
prerr_string "warning: "
The “prerr” prefix consistently indicates stderr output across the standard library.
Printf-Style Formatted Output
OCaml’s Printf module provides type-safe formatted printing:
(* To stdout *)
Printf.printf "Name: %s, Age: %d, Score: %.2f\n" name age score
(* To stderr *)
Printf.eprintf "Error: %s (code %d)\n" msg code
Common format specifiers:
%s— string%d— integer%f— float.2f— float with 2 decimal places%b— boolean (true/false)%c— character%a— user-defined formatter (advanced)
The compiler checks that the number and types of arguments match the format string — a mismatch is a compile-time error, not a runtime crash.
Using Format for Pretty-Printing
For structured output with indentation and alignment, OCaml’s Format module is the standard approach:
let print_person name age =
Format.printf "Person {@[@ name: %s;@ age: %d@]@,}@."
name age
The Format module uses “pretty-print boxes” to manage layout:
- code>@[ — open a box
- code>@] — close a box
- code>@, — break hint (may become a newline)
- code>@; — break hint with custom spacing
- code>@. — flush and newline
Redirecting Output Programmatically
You can redirect stdout or stderr to a file:
let with_output_to filename f =
let oc = open_out filename in
let old_stdout = Unix.dup Unix.stdout in
Unix.dup2 (Unix.descr_of_out_channel oc) Unix.stdout;
let result = f () in
Unix.dup2 old_stdout Unix.stdout;
close_out oc;
result
(* Usage *)
let () =
with_output_to "log.txt" (fun () ->
print_endline "This goes to the file";
42
)
Using the Logs Library
For production applications, the logs library provides structured logging with level filtering:
(* Install: opam install logs *)
(* In your application *)
Logs.info (fun m -> m "Server started on port %d" port);
Logs.warn (fun m -> m "Connection timeout after %.1f seconds" elapsed);
Logs.err (fun m -> m "Failed to open %s: %a" filename Fmt.exn exn);
(* Set log level *)
Logs.set_level (Some Logs.Warning); (* Only warn and error *)
Quick Reference
print_endline— stdout with newlineprint_string— stdout without newlineprerr_endline— stderr with newlineprerr_string— stderr without newlinePrintf.printf— formatted stdoutPrintf.eprintf— formatted stderrFormat.printf— pretty-printed stdoutLogs.info/warn/err— structured logging
For most command-line tools, print_endline and prerr_endline cover the common cases. Reach for Printf when you need formatting, and Format when you need structured output.
Flushing Output
OCaml buffers output for performance. If your program crashes or you need real-time output, flush explicitly:
print_string "Processing...";
flush stdout; (* Force immediate output *)
(* Do long computation *)
print_endline "done"
The print_endline and prerr_endline functions flush automatically. But print_string and prerr_string do not — you must call flush stdout or flush stderr manually.
This matters for progress indicators:
let show_progress i total =
let pct = 100 * i / total in
Printf.printf "\rProgress: %d%%" pct;
flush stdout
let () =
for i = 1 to 1000 do
show_progress i 1000;
Unix.sleepf 0.01
done;
print_endline ""
Capturing Output in Tests
When writing tests, you may want to capture stdout or stderr output:
let capture_stdout f =
let buf = Buffer.create 256 in
let old_stdout = stdout in
let new_stdout = Stdlib.open_out_gen [Open_wronly; Open_creat] 0 "/dev/null" in
(* Simplified: in practice use Unix.dup2 for proper capture *)
let result = f () in
result
(* Better approach: use the Camlunix or Core library's capture utilities *)
For testing, the simplest approach is often to use functions that return strings instead of printing, and only use printing in your main entry point:
(* Library function - returns string, easy to test *)
let format_result name score =
Printf.sprintf "Result: %s scored %.1f" name score
(* CLI entry point - does the printing *)
let () =
print_endline (format_result "Alice" 95.5)
This separation makes your code testable while still supporting formatted output.
