• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

devonfw / IDEasy / 22203456564

19 Feb 2026 10:51PM UTC coverage: 70.112% (-0.4%) from 70.474%
22203456564

Pull #1710

github

web-flow
Merge 97cf467e5 into 379acdc9d
Pull Request #1710: #404: allow logging via SLF4J

4065 of 6386 branches covered (63.65%)

Branch coverage included in aggregate %.

10580 of 14502 relevant lines covered (72.96%)

3.16 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

70.43
cli/src/main/java/com/devonfw/tools/ide/log/AbstractIdeSubLogger.java
1
package com.devonfw.tools.ide.log;
2

3
import com.devonfw.tools.ide.cli.CliException;
4

5
/**
6
 * Abstract base implementation of {@link IdeSubLogger}.
7
 */
8
public abstract class AbstractIdeSubLogger implements IdeSubLogger {
1✔
9

10
  /** @see #getLevel() */
11
  protected final IdeLogLevel level;
12

13
  protected final IdeLogExceptionDetails exceptionDetails;
14

15
  final IdeLogListener listener;
16

17
  protected boolean colored;
18

19
  private boolean enabled;
20

21
  private IdeLogArgFormatter argFormatter;
22

23
  /**
24
   * The constructor.
25
   *
26
   * @param level the {@link #getLevel() log-level}.
27
   * @param colored - see {@link #isColored()}.
28
   * @param exceptionDetails the {@link IdeLogExceptionDetails} configuring how to handle exceptions.
29
   * @param listener the {@link IdeLogListener} to send log-events to.
30
   */
31
  public AbstractIdeSubLogger(IdeLogLevel level, boolean colored, IdeLogExceptionDetails exceptionDetails, IdeLogListener listener) {
32

33
    super();
2✔
34
    this.level = level;
3✔
35
    this.exceptionDetails = exceptionDetails;
3✔
36
    this.argFormatter = IdeLogArgFormatter.DEFAULT;
3✔
37
    if (listener == null) {
2✔
38
      this.listener = IdeLogListenerNone.INSTANCE;
4✔
39
    } else {
40
      this.listener = listener;
3✔
41
    }
42
    this.colored = colored;
3✔
43
    this.enabled = true;
3✔
44
  }
1✔
45

46
  @Override
47
  public IdeLogLevel getLevel() {
48

49
    return this.level;
×
50
  }
51

52
  @Override
53
  public boolean isEnabled() {
54

55
    return this.enabled;
3✔
56
  }
57

58
  void setEnabled(boolean enabled) {
59

60
    this.enabled = enabled;
3✔
61
  }
1✔
62

63
  void setColored(boolean colored) {
64

65
    this.colored = colored;
3✔
66
  }
1✔
67

68
  /**
69
   * Should only be used internally by logger implementation.
70
   *
71
   * @param message the message template.
72
   * @param args the dynamic arguments to fill in.
73
   * @return the resolved message with the parameters filled in.
74
   */
75
  protected String compose(String message, Object... args) {
76
    return compose(this.argFormatter, this::invalidMessage, message, args);
8✔
77
  }
78

79
  /**
80
   * Should only be used internally by logger implementation.
81
   *
82
   * @param message the message template.
83
   * @param args the dynamic arguments to fill in.
84
   * @return the resolved message with the parameters filled in.
85
   */
86
  static String compose(IdeLogArgFormatter formatter, InvalidLogMessageHandler handler, String message, Object... args) {
87

88
    int pos = message.indexOf("{}");
4✔
89
    if (pos < 0) {
2!
90
      if (args.length > 0) {
×
91
        handler.invalidMessage(message, false, args);
×
92
      }
93
      return message;
×
94
    }
95
    int argIndex = 0;
2✔
96
    int start = 0;
2✔
97
    int length = message.length();
3✔
98
    StringBuilder sb = new StringBuilder(length + 48);
7✔
99
    while (pos >= 0) {
2✔
100
      sb.append(message, start, pos);
6✔
101
      sb.append(formatter.formatArgument(args[argIndex++]));
9✔
102
      start = pos + 2;
4✔
103
      pos = message.indexOf("{}", start);
5✔
104
      if ((argIndex >= args.length) && (pos > 0)) {
6!
105
        handler.invalidMessage(message, true, args);
×
106
        pos = -1;
×
107
      }
108
    }
109
    if (start < length) {
3✔
110
      String rest = message.substring(start);
4✔
111
      sb.append(rest);
4✔
112
    }
113
    if (argIndex < args.length) {
4!
114
      handler.invalidMessage(message, false, args);
×
115
    }
116
    return sb.toString();
3✔
117
  }
118

119
  private void invalidMessage(String message, boolean more, Object[] args) {
120

121
    warning("Invalid log message with " + args.length + " argument(s) but " + (more ? "more" : "less")
×
122
        + " placeholders: " + message);
123
  }
×
124

125
  private void warning(String message) {
126

127
    boolean colored = isColored();
×
128
    if (colored) {
×
129
      System.err.print(IdeLogLevel.ERROR.getEndColor());
×
130
      System.err.print(IdeLogLevel.ERROR.getStartColor());
×
131
    }
132
    System.err.println(message);
×
133
    if (colored) {
×
134
      System.err.print(IdeLogLevel.ERROR.getEndColor());
×
135
    }
136
  }
×
137

138
  /**
139
   * @return {@code true} if colored logging is used, {@code false} otherwise.
140
   */
141
  public boolean isColored() {
142

143
    return this.colored;
×
144
  }
145

146
  @Override
147
  public String log(Throwable error, String message, Object... args) {
148

149
    if (!this.enabled) {
3✔
150
      // performance optimization: do not fill in arguments if disabled
151
      return message;
2✔
152
    }
153
    String actualMessage = message;
2✔
154
    if (error != null) {
2✔
155
      if (isOmitStacktrace(error)) {
4✔
156
        if (message == null) {
2!
157
          actualMessage = error.getMessage();
3✔
158
        }
159
        error = null;
3✔
160
      } else if (message == null) {
2✔
161
        actualMessage = error.toString();
3✔
162
      }
163
    }
164
    if (actualMessage == null) {
2!
165
      actualMessage = "Internal error: Both message and error is null - nothing to log!";
×
166
      // fail fast if assertions are enabled, so developers of IDEasy will find the bug immediately but in productive use better log the error and continue
167
      assert false : actualMessage;
×
168
    } else if ((args != null) && (args.length > 0)) {
5✔
169
      actualMessage = compose(actualMessage, args);
5✔
170
    }
171
    boolean accept = this.listener.onLog(this.level, actualMessage, message, args, error);
10✔
172
    if (accept) {
2✔
173
      doLog(actualMessage, error);
4✔
174
    }
175
    return actualMessage;
2✔
176
  }
177

178
  private boolean isOmitStacktrace(Throwable error) {
179

180
    return (error instanceof CliException);
3✔
181
  }
182

183
  /**
184
   * @param message the formatted message to log.
185
   * @param error the optional {@link Throwable} to log or {@code null} for no error.
186
   */
187
  protected abstract void doLog(String message, Throwable error);
188

189
  /**
190
   * @param argFormatter the new {@link IdeLogArgFormatter} to use.
191
   */
192
  void setArgFormatter(IdeLogArgFormatter argFormatter) {
193

194
    this.argFormatter = argFormatter;
3✔
195
  }
1✔
196

197
  @Override
198
  public String toString() {
199

200
    return getClass().getSimpleName() + "@" + this.level;
×
201
  }
202

203
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc