001    /*
002     * $Id: PerfLogger.java,v 1.8 2014/05/17 06:55:25 oboehm Exp $
003     *
004     * Copyright (c) 2014 by Oliver Boehm
005     *
006     * Licensed under the Apache License, Version 2.0 (the "License");
007     * you may not use this file except in compliance with the License.
008     * You may obtain a copy of the License at
009     *
010     *   http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing, software
013     * distributed under the License is distributed on an "AS IS" BASIS,
014     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express orimplied.
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     *
018     * (c)reated 17.03.2014 by oliver (ob@oasd.de)
019     */
020    
021    package patterntesting.runtime.log;
022    
023    import org.slf4j.*;
024    
025    import patterntesting.runtime.monitor.ProfileMonitor;
026    import patterntesting.runtime.util.Converter;
027    
028    /**
029     * This class is a kind of performance logger to be able to log the execution
030     * times of methods or code segments which may need a little bit longer. It can
031     * be used as a replacement of a normal logger.
032     * <p>
033     * We use a {@link LogWatch} to measure the times between a
034     * {@link #start(String, Object...)} and {@link #end(String, Object...)} call.
035     * We store this {@link LogWatch} as {@link ThreadLocal} because loggers are
036     * normally used as static variables. So we can be sure that each thread has
037     * its own {@link LogWatch}.
038     * </p>
039     *
040     * @author oliver
041     * @since 1.4.1 (17.03.2014)
042     * @version $Revision: 1.8 $
043     */
044    public final class PerfLogger {
045    
046        private final Logger logger;
047    
048        /** The local stopwatch. */
049        private final ThreadLocal<LogWatch> timer = new ThreadLocal<LogWatch>() {
050            @Override
051            protected LogWatch initialValue() {
052                LogWatch watch = new LogWatch();
053                return watch;
054            }
055        };
056    
057        /**
058         * Instantiates a new perf logger.
059         */
060        public PerfLogger() {
061            this(PerfLogger.class);
062        }
063    
064        /**
065         * Instantiates a new perf logger.
066         *
067         * @param clazz the clazz
068         */
069        public PerfLogger(final Class<?> clazz) {
070            this(LoggerFactory.getLogger(clazz));
071        }
072    
073        /**
074         * Instantiates a new perf logger.
075         *
076         * @param logger the logger
077         */
078        public PerfLogger(final Logger logger) {
079            this.logger = logger;
080        }
081    
082        /**
083         * Start the log for the given message. The default level for the logged
084         * message is "INFO".
085         *
086         * @param format the format
087         * @param args the args
088         */
089        public void start(final String format, final Object... args) {
090            this.start(SimpleLog.LOG_LEVEL_INFO, format, args);
091        }
092    
093        /**
094         * Start the log for the given message. For the valid log levels see
095         *
096         * @param level the level
097         * @param format the format
098         * @param args the args
099         * {@link SimpleLog}.
100         * @see SimpleLog
101         */
102        public void start(final int level, final String format, final Object... args) {
103            this.log(level, format, args);
104            timer.get().start();
105        }
106    
107        /**
108         * End the log with the given message. The output will contain the measure
109         * time from the last {@link #start(String, Object...)} log. So be sure to
110         * call one of the start methods before.
111         * <p>
112         * The default level for the logged message is "INFO".
113         * </p>
114         *
115         * @param format the format
116         * @param args the args
117         */
118        public void end(final String format, final Object... args) {
119            this.end(SimpleLog.LOG_LEVEL_INFO, format, args);
120        }
121    
122        /**
123         * End the log with the given message. The output will contain the measure
124         * time from the last {@link #start(String, Object...)} log. So be sure to
125         * call one of the start methods before.
126         * <p>
127         * It is recommended to use the same as in
128         *
129         * @param level the level
130         * @param format the format
131         * @param args the args
132         * {@link #start(int, String, Object...)}. If the reported times is greater
133         * than 1 minute the log level will be increased at least to "INFO".
134         * </p>
135         */
136        public void end(int level, final String format, final Object... args) {
137            LogWatch watch = timer.get();
138            watch.stop();
139            long millis = watch.getElapsedTime();
140            if ((millis > 60000L) && (level < SimpleLog.LOG_LEVEL_INFO)) {
141                this.logger.trace("Log level will be increased from {} to 'INFO'", level);
142                level = SimpleLog.LOG_LEVEL_INFO;
143            }
144            this.log(level, format + " finished after " + watch + ".", args);
145        }
146    
147        /**
148         * Log.
149         *
150         * @param level the level
151         * @param format the format
152         * @param args the args
153         */
154        public void log(final int level, final String format, final Object... args) {
155            switch (level) {
156                case SimpleLog.LOG_LEVEL_TRACE:
157                    this.logger.trace(format, args);
158                    break;
159                case SimpleLog.LOG_LEVEL_DEBUG:
160                    this.logger.debug(format, args);
161                    break;
162                case SimpleLog.LOG_LEVEL_INFO:
163                    this.logger.info(format, args);
164                    break;
165                case SimpleLog.LOG_LEVEL_WARN:
166                    this.logger.warn(format, args);
167                    break;
168                case SimpleLog.LOG_LEVEL_ERROR:
169                case SimpleLog.LOG_LEVEL_FATAL:
170                    this.logger.trace(format, args);
171                    break;
172                default:
173                    this.logger.info("Level " + level + ": " + format, args);
174                    break;
175            }
176        }
177    
178        /**
179         * Error.
180         *
181         * @param format the format
182         * @param args the args
183         */
184        public void error(final String format, final Object... args) {
185            logger.error(format, args);
186        }
187    
188        /**
189         * Warn.
190         *
191         * @param format the format
192         * @param args the args
193         */
194        public void warn(final String format, final Object... args) {
195            logger.warn(format, args);
196        }
197    
198        /**
199         * Info.
200         *
201         * @param format the format
202         * @param args the args
203         */
204        public void info(final String format, final Object... args) {
205            logger.info(format, args);
206        }
207    
208        /**
209         * Debug.
210         *
211         * @param format the format
212         * @param args the args
213         */
214        public void debug(final String format, final Object... args) {
215            logger.debug(format, args);
216        }
217    
218        /**
219         * Stops the given 'mon' and logs the given command with the needed time if
220         * debug is enabled.
221         *
222         * @param mon the mon
223         * @param command the command
224         * @deprecated moved to SqlStatistic class
225         */
226        @Deprecated
227        public void stop(final ProfileMonitor mon, final String command) {
228            stop(mon, command, Void.TYPE);
229        }
230    
231        /**
232         * Stops the given 'mon' and logs the given command with the needed time if
233         * debug is enabled.
234         *
235         * @param mon the mon
236         * @param command the command
237         * @param returnValue the return value
238         * @deprecated moved to SqlStatistic class
239         */
240        @Deprecated
241        public void stop(final ProfileMonitor mon, final String command, final Object returnValue) {
242            mon.stop();
243            if (logger.isDebugEnabled()) {
244                logger.debug("\"{}\" returned with {} after {}.", command,
245                        Converter.toShortString(returnValue), mon.getLastTime());
246            }
247        }
248    
249        /**
250         * Trace.
251         *
252         * @param format the format
253         * @param args the args
254         */
255        public void trace(final String format, final Object... args) {
256            logger.trace(format, args);
257        }
258    
259        /**
260         * Checks if is debug enabled.
261         *
262         * @return true, if is debug enabled
263         */
264        public boolean isDebugEnabled() {
265            return logger.isDebugEnabled();
266        }
267    
268    }
269