Skip to content

Comments

Add configurable output stream to AnsiLog to avoid hardcoded System.out#3128

Merged
hengyunabc merged 2 commits intomasterfrom
copilot/add-configurable-out-field
Jan 9, 2026
Merged

Add configurable output stream to AnsiLog to avoid hardcoded System.out#3128
hengyunabc merged 2 commits intomasterfrom
copilot/add-configurable-out-field

Conversation

Copy link
Contributor

Copilot AI commented Jan 9, 2026

AnsiLog currently hardcodes System.out, which pollutes host stdout for embedded agents. This adds a configurable output stream while keeping default behavior.

  • Configurable output: Introduced a volatile PrintStream out with setters/getters to override the log sink; defaults to System.out.
  • Unified writes: All log printing and stack trace outputs now use the configurable stream.
// Redirect AnsiLog output
PrintStream prev = AnsiLog.out(new PrintStream("arthas.log"));
// ...
AnsiLog.out(prev); // restore if needed
Original prompt

This section details on the original issue you should resolve

<issue_title>Add a configurable out field to AnsiLog, similar to the existing LEVEL field</issue_title>
<issue_description>- [ ] 我已经在 issues 里搜索,没有重复的issue。

环境信息

When Arthas is embedded in other applications (e.g., OpenTelemetry Java Agent,
Spring Boot applications), AnsiLog outputs directly to System.out, which:

  1. Pollutes the host application's stdout
  2. Interferes with log collection systems
  3. Cannot be redirected to a separate file

The official AgentBootstrap already uses a configurable PrintStream pattern,
but AnsiLog still hardcodes System.out.

Solution

Add a configurable out field to AnsiLog, similar to the existing LEVEL field:

  • Default: System.out (backward compatible)
  • API: AnsiLog.out(PrintStream) / AnsiLog.out()
  • Thread-safe with volatile

期望的结果

diff --git a/common/src/main/java/com/taobao/arthas/common/AnsiLog.java b/common/src/main/java/com/taobao/arthas/common/AnsiLog.java
index xxx..yyy 100644
--- a/common/src/main/java/com/taobao/arthas/common/AnsiLog.java
+++ b/common/src/main/java/com/taobao/arthas/common/AnsiLog.java
@@ -1,5 +1,6 @@
 package com.taobao.arthas.common;
 
+import java.io.PrintStream;
 import java.util.logging.Level;
 import java.util.regex.Matcher;
 
@@ -20,6 +21,12 @@ public abstract class AnsiLog {
 
     static boolean enableColor;
 
+    /**
+     * Output stream for log messages, defaults to System.out.
+     * Can be configured via {@link #out(PrintStream)} for embedded scenarios.
+     */
+    private static volatile PrintStream out = System.out;
+
     public static java.util.logging.Level LEVEL = java.util.logging.Level.CONFIG;
 
     private static final String RESET = "\033[0m";
@@ -76,6 +83,28 @@ public abstract class AnsiLog {
         return enableColor;
     }
 
+    /**
+     * Set the output stream for log messages.
+     *
+     * @param printStream the output stream, null to use System.out
+     * @return the previous output stream
+     */
+    public static PrintStream out(PrintStream printStream) {
+        PrintStream old = out;
+        out = (printStream != null) ? printStream : System.out;
+        return old;
+    }
+
+    /**
+     * Get the current output stream.
+     *
+     * @return the current output stream
+     */
+    public static PrintStream out() {
+        return out;
+    }
+
     /**
      * set logger Level
      *
@@ -144,9 +173,9 @@ public abstract class AnsiLog {
     public static void trace(String msg) {
         if (canLog(Level.FINEST)) {
             if (enableColor) {
-                System.out.println(TRACE_COLOR_PREFIX + msg);
+                out.println(TRACE_COLOR_PREFIX + msg);
             } else {
-                System.out.println(TRACE_PREFIX + msg);
+                out.println(TRACE_PREFIX + msg);
             }
         }
     }
@@ -159,7 +188,7 @@ public abstract class AnsiLog {
 
     public static void trace(Throwable t) {
         if (canLog(Level.FINEST)) {
-            t.printStackTrace(System.out);
+            t.printStackTrace(out);
         }
     }
 
@@ -167,9 +196,9 @@ public abstract class AnsiLog {
     public static void debug(String msg) {
         if (canLog(Level.FINER)) {
             if (enableColor) {
-                System.out.println(DEBUG_COLOR_PREFIX + msg);
+                out.println(DEBUG_COLOR_PREFIX + msg);
             } else {
-                System.out.println(DEBUG_PREFIX + msg);
+                out.println(DEBUG_PREFIX + msg);
             }
         }
     }
@@ -182,7 +211,7 @@ public abstract class AnsiLog {
 
     public static void debug(Throwable t) {
         if (canLog(Level.FINER)) {
-            t.printStackTrace(System.out);
+            t.printStackTrace(out);
         }
     }
 
@@ -190,9 +219,9 @@ public abstract class AnsiLog {
     public static void info(String msg) {
         if (canLog(Level.CONFIG)) {
             if (enableColor) {
-                System.out.println(INFO_COLOR_PREFIX + msg);
+                out.println(INFO_COLOR_PREFIX + msg);
             } else {
-                System.out.println(INFO_PREFIX + msg);
+                out.println(INFO_PREFIX + msg);
             }
         }
     }
@@ -205,7 +234,7 @@ public abstract class AnsiLog {
 
     public static void info(Throwable t) {
         if (canLog(Level.CONFIG)) {
-            t.printStackTrace(System.out);
+            t.printStackTrace(out);
         }
     }
 
@@ -213,9 +242,9 @@ public abstract class AnsiLog {
     public static void warn(String msg) {
         if (canLog(Level.WARNING)) {
             if (enableColor) {
-                System.out.println(WARN_COLOR_PREFIX + msg);
+                out.println(WARN_COLOR_PREFIX + msg);
             } else {
-                System.out.println(WARN_PREFIX + msg);
+                out.println(WARN_PREFIX + msg);
             }
         }
     }
@@ -228,...

</details>



<!-- START COPILOT CODING AGENT SUFFIX -->

- Fixes alibaba/arthas#3126

<!-- START COPILOT CODING AGENT TIPS -->
---

💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more [Copilot coding agent tips](https://gh.io/copilot-coding-agent-tips) in the docs.

@CLAassistant
Copy link

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

Co-authored-by: hengyunabc <1683936+hengyunabc@users.noreply.github.com>
Copilot AI changed the title [WIP] Add configurable out field to AnsiLog Add configurable output stream to AnsiLog to avoid hardcoded System.out Jan 9, 2026
Copilot AI requested a review from hengyunabc January 9, 2026 13:05
@hengyunabc hengyunabc marked this pull request as ready for review January 9, 2026 13:11
@hengyunabc hengyunabc merged commit 896fc7e into master Jan 9, 2026
14 of 15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants