001/*
002 * Logback: the reliable, generic, fast and flexible logging framework.
003 * Copyright (C) 1999-2024, QOS.ch. All rights reserved.
004 *
005 * This program and the accompanying materials are dual-licensed under
006 * either the terms of the Eclipse Public License v1.0 as published by
007 * the Eclipse Foundation
008 *
009 *   or (per the licensee's choosing)
010 *
011 * under the terms of the GNU Lesser General Public License version 2.1
012 * as published by the Free Software Foundation.
013 */
014
015package ch.qos.logback.core.util;
016
017import ch.qos.logback.core.Context;
018import ch.qos.logback.core.CoreConstants;
019import ch.qos.logback.core.helpers.ThrowableToStringArray;
020import ch.qos.logback.core.status.ErrorStatus;
021import ch.qos.logback.core.status.Status;
022import ch.qos.logback.core.status.StatusManager;
023import ch.qos.logback.core.status.StatusUtil;
024
025import java.io.PrintStream;
026import java.util.Iterator;
027import java.util.List;
028
029import static ch.qos.logback.core.status.StatusUtil.filterStatusListByTimeThreshold;
030
031/**
032 *
033 * Same as StatusPrinter but with instance methods instead of static.
034 *
035 * @since 1.5.4
036 */
037public class StatusPrinter2 {
038
039
040    private PrintStream ps = System.out;
041
042    static CachingDateFormatter cachingDateFormat = new CachingDateFormatter("HH:mm:ss,SSS");
043
044    public void setPrintStream(PrintStream printStream) {
045        ps = printStream;
046    }
047
048    /**
049     * Print the contents of the context statuses, but only if they contain warnings
050     * or errors.
051     *
052     * @param context a context to print
053     */
054    public void printInCaseOfErrorsOrWarnings(Context context) {
055        printInCaseOfErrorsOrWarnings(context, 0);
056    }
057
058    /**
059     * Print the contents of the context status, but only if they contain warnings
060     * or errors occurring later than the threshold.
061     *
062     * @param context a context to print
063     * @param threshold filter events later than the threshold
064     */
065    public void printInCaseOfErrorsOrWarnings(Context context, long threshold) {
066        if (context == null) {
067            throw new IllegalArgumentException("Context argument cannot be null");
068        }
069
070        StatusManager sm = context.getStatusManager();
071        if (sm == null) {
072            ps.println("WARN: Context named \"" + context.getName() + "\" has no status manager");
073        } else {
074            StatusUtil statusUtil = new StatusUtil(context);
075            if (statusUtil.getHighestLevel(threshold) >= ErrorStatus.WARN) {
076                print(sm, threshold);
077            }
078        }
079    }
080
081    /**
082     * Print the contents of the context statuses, but only if they contain errors.
083     *
084     * @param context  a context to print
085     */
086    public void printIfErrorsOccured(Context context) {
087        if (context == null) {
088            throw new IllegalArgumentException("Context argument cannot be null");
089        }
090
091        StatusManager sm = context.getStatusManager();
092        if (sm == null) {
093            ps.println("WARN: Context named \"" + context.getName() + "\" has no status manager");
094        } else {
095            StatusUtil statusUtil = new StatusUtil(context);
096            if (statusUtil.getHighestLevel(0) == ErrorStatus.ERROR) {
097                print(sm);
098            }
099        }
100    }
101
102    /**
103     * Print the contents of the context's status data.
104     *
105     * @param context a context to print
106     */
107    public void print(Context context) {
108        print(context, 0);
109    }
110
111    /**
112     * Print context's status data with a timestamp higher than the threshold.
113     *
114     * @param context a context to print
115     */
116    public  void print(Context context, long threshold) {
117        if (context == null) {
118            throw new IllegalArgumentException("Context argument cannot be null");
119        }
120
121        StatusManager sm = context.getStatusManager();
122        if (sm == null) {
123            ps.println("WARN: Context named \"" + context.getName() + "\" has no status manager");
124        } else {
125            print(sm, threshold);
126        }
127    }
128
129    public void print(StatusManager sm) {
130        print(sm, 0);
131    }
132
133    public  void print(StatusManager sm, long threshold) {
134        StringBuilder sb = new StringBuilder();
135        List<Status> filteredList = filterStatusListByTimeThreshold(sm.getCopyOfStatusList(), threshold);
136        buildStrFromStatusList(sb, filteredList);
137        ps.println(sb.toString());
138    }
139
140    public void print(List<Status> statusList) {
141        StringBuilder sb = new StringBuilder();
142        buildStrFromStatusList(sb, statusList);
143        ps.println(sb.toString());
144    }
145
146    private void buildStrFromStatusList(StringBuilder sb, List<Status> statusList) {
147        if (statusList == null)
148            return;
149        for (Status s : statusList) {
150            buildStr(sb, "", s);
151        }
152    }
153
154    private void appendThrowable(StringBuilder sb, Throwable t) {
155        String[] stringRep = ThrowableToStringArray.convert(t);
156
157        for (String s : stringRep) {
158            if (s.startsWith(CoreConstants.CAUSED_BY)) {
159                // nothing
160            } else if (Character.isDigit(s.charAt(0))) {
161                // if line resembles "48 common frames omitted"
162                sb.append("\t... ");
163            } else {
164                // most of the time. just add a tab+"at"
165                sb.append("\tat ");
166            }
167            sb.append(s).append(CoreConstants.LINE_SEPARATOR);
168        }
169    }
170
171    public void buildStr(StringBuilder sb, String indentation, Status s) {
172        String prefix;
173        if (s.hasChildren()) {
174            prefix = indentation + "+ ";
175        } else {
176            prefix = indentation + "|-";
177        }
178
179        if (cachingDateFormat != null) {
180            String dateStr = cachingDateFormat.format(s.getTimestamp());
181            sb.append(dateStr).append(" ");
182        }
183        sb.append(prefix).append(s).append(CoreConstants.LINE_SEPARATOR);
184
185        if (s.getThrowable() != null) {
186            appendThrowable(sb, s.getThrowable());
187        }
188        if (s.hasChildren()) {
189            Iterator<Status> ite = s.iterator();
190            while (ite.hasNext()) {
191                Status child = ite.next();
192                buildStr(sb, indentation + "  ", child);
193            }
194        }
195    }
196}