diff --git a/nix/internal-tests.nix b/nix/internal-tests.nix
index 38e6e924e0b2868a34b0cc9f5ac80d0541c7f969..ebce2ac5341483ccf0b8dddeaae5bde1ce99435a 100644
--- a/nix/internal-tests.nix
+++ b/nix/internal-tests.nix
@@ -54,6 +54,7 @@
 , perl
 , python3
 , python3Packages
+, socat
 , swi-prolog
 , time
 , unixtools
@@ -128,6 +129,7 @@ stdenvNoCC.mkDerivation rec {
     python3
     python3Packages.jsonschema
     python3Packages.pyaml
+    socat
     swi-prolog
     time
     unixtools.getopt
diff --git a/nix/mk_tests.nix b/nix/mk_tests.nix
index 81e3c3f6a6e2b65859b8a075e24cd4ad24ec783d..b24ee61011990023511e357fa04a25bb582cde0a 100644
--- a/nix/mk_tests.nix
+++ b/nix/mk_tests.nix
@@ -34,6 +34,7 @@
 , jq
 , perl
 , python3Packages
+, socat
 , stdenvNoCC
 , time
 , unixtools
@@ -65,6 +66,7 @@ stdenvNoCC.mkDerivation {
     perl
     python3Packages.jsonschema
     python3Packages.pyaml
+    socat
     time
     unixtools.getopt
     which
diff --git a/src/kernel_services/plugin_entry_points/log.ml b/src/kernel_services/plugin_entry_points/log.ml
index 10a4ec0697ffe7c600de4fb8a291f9e07e6d8159..88146911ff78da13a9b13aacbe18f0fa025d3661 100644
--- a/src/kernel_services/plugin_entry_points/log.ml
+++ b/src/kernel_services/plugin_entry_points/log.ml
@@ -20,6 +20,9 @@
 (*                                                                        *)
 (**************************************************************************)
 
+(* Messages longer than N characters are truncated when printed on terminal. *)
+let max_message_length = 10000
+
 type kind = Result | Feedback | Debug | Warning | Error | Failure
 
 [@@@ warning "-32"]
@@ -467,6 +470,7 @@ let logtransient channel text =
        try
          Format.pp_print_newline fmt () ;
          Format.pp_print_flush fmt () ;
+         ignore (Rich_text.truncate buffer max_message_length) ;
          let p,q = Rich_text.trim buffer in
          do_transient channel.terminal (Rich_text.contents buffer) p q ;
          close_buffer channel
@@ -497,11 +501,19 @@ let logwithfinal finally channel
          Format.pp_close_box fmt () ;
          Format.pp_print_newline fmt () ;
          Format.pp_print_flush fmt () ;
+         let truncated =
+           if channel.terminal.isatty
+           then Rich_text.truncate buffer max_message_length
+           else false
+         in
          let p,q = Rich_text.trim buffer in
          let output =
            if p <= q then
              let source = get_source current source in
              let message = Rich_text.range buffer p q in
+             let message =
+               if truncated then "(truncated message) " ^ message else message
+             in
              let event = {
                evt_kind = kind ;
                evt_plugin = channel.plugin ;
diff --git a/src/libraries/utils/rich_text.ml b/src/libraries/utils/rich_text.ml
index 3d08c1cd3f6977288219889a6c0c9e60f1d39411..1e1d2220129f994acf95965d78108d1b8b650a22 100644
--- a/src/libraries/utils/rich_text.ml
+++ b/src/libraries/utils/rich_text.ml
@@ -106,8 +106,6 @@ let pretty ?vbox fmt message =
 (* -------------------------------------------------------------------------- *)
 
 let min_buffer = 128    (* initial size of buffer *)
-let max_buffer = 2097152 (* maximal size of buffer *)
-let tgr_buffer = 3145728 (* elasticity (internal overhead) *)
 
 type buffer = {
   mutable formatter : Format.formatter ; (* formatter on self (recursive) *)
@@ -136,7 +134,8 @@ let shrink buffer =
   if Buffer.length buffer.content > min_buffer then
     Buffer.reset buffer.content
 
-let truncate_text buffer size =
+let truncate buffer size =
+  let truncated = ref false in
   if Buffer.length buffer.content > size then
     begin
       let p = trim_begin buffer in
@@ -147,23 +146,21 @@ let truncate_text buffer size =
       else if n <= size then
         Buffer.blit buffer.content p (Buffer.to_bytes buffer.content) 0 n
       else
-        begin
-          let n_left = size / 2 - 3 in
-          let n_right = size - n_left - 5 in
-          if p > 0 then
-            Buffer.blit buffer.content p (Buffer.to_bytes buffer.content) 0 n_left;
-          let buf_right = Buffer.sub buffer.content (q-n_right+1) n_right in
-          Buffer.truncate buffer.content n_left;
-          Buffer.add_string buffer.content "[...]";
-          Buffer.add_string buffer.content buf_right;
-        end
-    end
+        let n_left = size / 2 - 3 in
+        let n_right = size - n_left - 5 in
+        if p > 0 then
+          Buffer.blit buffer.content p (Buffer.to_bytes buffer.content) 0 n_left;
+        let buf_right = Buffer.sub buffer.content (q-n_right+1) n_right in
+        Buffer.truncate buffer.content n_left;
+        Buffer.add_string buffer.content "[...]";
+        Buffer.add_string buffer.content buf_right;
+        truncated := true;
+    end;
+  !truncated
 
 (* All text added shall go through this function *)
 let append buffer s k n =
-  Buffer.add_substring buffer.content s k n ;
-  if Buffer.length buffer.content > tgr_buffer then
-    truncate_text buffer max_buffer
+  Buffer.add_substring buffer.content s k n
 
 let push_tag buffer _tag =
   let p = Buffer.length buffer.content in
@@ -219,13 +216,12 @@ let create ?indent ?margin () =
   buffer
 
 let trim buffer =
-  truncate_text buffer max_buffer ;
   let p = trim_begin buffer in
   let q = trim_end buffer in
   p , q
 
 let contents buffer =
-  truncate_text buffer max_buffer ; Buffer.contents buffer.content
+  Buffer.contents buffer.content
 
 let message buffer =
   ( Buffer.contents buffer.content , List.rev buffer.revtags )
@@ -247,7 +243,6 @@ let to_string ?(indent=20) ?(margin=40) ?(trim=true) pp data =
   let fmt = formatter buffer in
   pp fmt data ;
   Format.pp_print_flush fmt () ;
-  truncate_text buffer max_buffer ;
   if trim then
     let p = trim_begin buffer in
     let q = trim_end buffer in
diff --git a/src/libraries/utils/rich_text.mli b/src/libraries/utils/rich_text.mli
index 864f9647854cd605373bea71078e3501e95a2189..0290a56ff053ceff3bf8b15d39f052a0cec4d1d0 100644
--- a/src/libraries/utils/rich_text.mli
+++ b/src/libraries/utils/rich_text.mli
@@ -106,6 +106,10 @@ val trim : buffer -> int * int
 (** Resize the buffer to roughly fit its actual content. *)
 val shrink : buffer -> unit
 
+(** [truncate buffer size] truncates the content of [buffer] if longer than
+    [size] characters. Returns true if the bufffer has been truncated. *)
+val truncate : buffer -> int -> bool
+
 (* -------------------------------------------------------------------------- *)
 (** Printer *)
 (* -------------------------------------------------------------------------- *)
diff --git a/tests/misc/dune b/tests/misc/dune
index 1ac66020dfef15db60a3f578e47e78b01f746433..04a2773384edb245d309668e598586ab6071a911 100644
--- a/tests/misc/dune
+++ b/tests/misc/dune
@@ -8,3 +8,8 @@
   (enabled_if %{bin-available:gcc})
   (deps pragma-pack.c pragma-pack-utils.h)
 )
+
+(cram
+  (applies_to log-truncation)
+  (enabled_if %{bin-available:socat})
+)
diff --git a/tests/misc/log-truncation.t/dune b/tests/misc/log-truncation.t/dune
new file mode 100644
index 0000000000000000000000000000000000000000..01bb108622d6e01041363964775229012333d218
--- /dev/null
+++ b/tests/misc/log-truncation.t/dune
@@ -0,0 +1,8 @@
+(executable
+  (name "log_truncation")
+  (modules "log_truncation")
+  (modes plugin)
+  (libraries frama-c.init.cmdline frama-c.kernel)
+  (flags -open Frama_c_kernel :standard)
+  (promote (until-clean))
+)
diff --git a/tests/misc/log-truncation.t/dune-project b/tests/misc/log-truncation.t/dune-project
new file mode 100644
index 0000000000000000000000000000000000000000..f721e56b4bb4f5de0235f762e96a9f84ebd48f30
--- /dev/null
+++ b/tests/misc/log-truncation.t/dune-project
@@ -0,0 +1,3 @@
+(lang dune 3.13)
+(name frama-c-log-truncation)
+(package (name frama-c-log-truncation))
diff --git a/tests/misc/log-truncation.t/log_truncation.ml b/tests/misc/log-truncation.t/log_truncation.ml
new file mode 100644
index 0000000000000000000000000000000000000000..72a094bcd069acce2cc9bf68b2bbe578c9337f5a
--- /dev/null
+++ b/tests/misc/log-truncation.t/log_truncation.ml
@@ -0,0 +1,13 @@
+let run () =
+  (* This can be filtered to avoid too long test oracles. *)
+  let very_long_line = String.make 5000 '#' in
+  let line = "begin_line <-----" ^ very_long_line ^ "-----> end_line" in
+  let middle_text =
+    "[ This text should only appear in the log if is is redirected to a file. ]"
+  in
+  Kernel.warning
+    "This is a very, very long message intended to test the truncation \
+     of the Frama-C log: \n\
+     @[<v>%s@;%s@;%s@]" line middle_text line
+
+let () = Boot.Main.extend run
diff --git a/tests/misc/log-truncation.t/run.t b/tests/misc/log-truncation.t/run.t
new file mode 100644
index 0000000000000000000000000000000000000000..d3bf022957c6ca38753b4bff9c9c547483a6e303
--- /dev/null
+++ b/tests/misc/log-truncation.t/run.t
@@ -0,0 +1,20 @@
+Compilation of the log_truncation.ml file.
+  $ dune build --cache=disabled --root . _build/default/log_truncation.cmxs
+
+  $ frama-c -no-autoload-plugins -load-module log_truncation.cmxs > file-log.txt
+
+The message should not be truncated as the output was redireted to a file.
+  $ cat file-log.txt | tr -d '#'
+  [kernel] Warning: This is a very, very long message intended to test the truncation of the Frama-C log: 
+    
+    begin_line <----------> end_line
+    [ This text should only appear in the log if is is redirected to a file. ]
+    begin_line <----------> end_line
+
+  $ socat EXEC:"frama-c -no-autoload-plugins -load-module log_truncation.cmxs",pty GOPEN:terminal-log.txt
+
+The message should be truncated.
+  $ cat terminal-log.txt | tr -d '#'
+  [kernel] Warning: (truncated message) This is a very, very long message intended to test the truncation of the Frama-C log: 
+    
+    begin_line <-----[...]-----> end_line