001/** 002 * Copyright (c) 2004-2011 QOS.ch 003 * All rights reserved. 004 * 005 * Permission is hereby granted, free of charge, to any person obtaining 006 * a copy of this software and associated documentation files (the 007 * "Software"), to deal in the Software without restriction, including 008 * without limitation the rights to use, copy, modify, merge, publish, 009 * distribute, sublicense, and/or sell copies of the Software, and to 010 * permit persons to whom the Software is furnished to do so, subject to 011 * the following conditions: 012 * 013 * The above copyright notice and this permission notice shall be 014 * included in all copies or substantial portions of the Software. 015 * 016 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 017 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 018 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 019 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 020 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 021 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 022 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 023 * 024 */ 025package org.slf4j.ext; 026 027import org.slf4j.Logger; 028import org.slf4j.Marker; 029import org.slf4j.MarkerFactory; 030import org.slf4j.helpers.FormattingTuple; 031import org.slf4j.helpers.MessageFormatter; 032import org.slf4j.spi.LocationAwareLogger; 033 034/** 035 * A utility that provides standard mechanisms for logging certain kinds of 036 * activities. 037 * 038 * @author Ralph Goers 039 * @author Ceki Gülcü 040 */ 041public class XLogger extends LoggerWrapper implements Logger { 042 043 private static final String FQCN = XLogger.class.getName(); 044 static Marker FLOW_MARKER = MarkerFactory.getMarker("FLOW"); 045 static Marker ENTRY_MARKER = MarkerFactory.getMarker("ENTRY"); 046 static Marker EXIT_MARKER = MarkerFactory.getMarker("EXIT"); 047 048 static Marker EXCEPTION_MARKER = MarkerFactory.getMarker("EXCEPTION"); 049 static Marker THROWING_MARKER = MarkerFactory.getMarker("THROWING"); 050 static Marker CATCHING_MARKER = MarkerFactory.getMarker("CATCHING"); 051 052 static String EXIT_MESSAGE_0 = "exit"; 053 static String EXIT_MESSAGE_1 = "exit with ({})"; 054 055 static String ENTRY_MESSAGE_0 = "entry"; 056 static String ENTRY_MESSAGE_1 = "entry with ({})"; 057 static String ENTRY_MESSAGE_2 = "entry with ({}, {})"; 058 static String ENTRY_MESSAGE_3 = "entry with ({}, {}, {})"; 059 static String ENTRY_MESSAGE_4 = "entry with ({}, {}, {}, {})"; 060 static int ENTRY_MESSAGE_ARRAY_LEN = 5; 061 static String[] ENTRY_MESSAGE_ARRAY = new String[ENTRY_MESSAGE_ARRAY_LEN]; 062 static { 063 ENTRY_MARKER.add(FLOW_MARKER); 064 EXIT_MARKER.add(FLOW_MARKER); 065 THROWING_MARKER.add(EXCEPTION_MARKER); 066 CATCHING_MARKER.add(EXCEPTION_MARKER); 067 068 ENTRY_MESSAGE_ARRAY[0] = ENTRY_MESSAGE_0; 069 ENTRY_MESSAGE_ARRAY[1] = ENTRY_MESSAGE_1; 070 ENTRY_MESSAGE_ARRAY[2] = ENTRY_MESSAGE_2; 071 ENTRY_MESSAGE_ARRAY[3] = ENTRY_MESSAGE_3; 072 ENTRY_MESSAGE_ARRAY[4] = ENTRY_MESSAGE_4; 073 } 074 075 public enum Level { 076 TRACE("TRACE", LocationAwareLogger.TRACE_INT), DEBUG("DEBUG", LocationAwareLogger.DEBUG_INT), INFO("INFO", LocationAwareLogger.INFO_INT), WARN("WARN", 077 LocationAwareLogger.WARN_INT), ERROR("ERROR", LocationAwareLogger.ERROR_INT); 078 079 private final String name; 080 private final int level; 081 082 public String toString() { 083 return this.name; 084 } 085 086 public int intValue() { 087 return this.level; 088 } 089 090 private Level(String name, int level) { 091 this.name = name; 092 this.level = level; 093 } 094 } 095 096 /** 097 * Given an underlying logger, construct an XLogger 098 * 099 * @param logger underlying logger 100 */ 101 public XLogger(Logger logger) { 102 // If class B extends A, assuming B does not override method x(), the caller 103 // of new B().x() is A and not B, see also 104 // http://jira.qos.ch/browse/SLF4J-105 105 super(logger, LoggerWrapper.class.getName()); 106 } 107 108 /** 109 * Log method entry. 110 * 111 * @param argArray supplied parameters 112 */ 113 public void entry(Object... argArray) { 114 if (instanceofLAL && logger.isTraceEnabled(ENTRY_MARKER)) { 115 String messagePattern = null; 116 if (argArray.length < ENTRY_MESSAGE_ARRAY_LEN) { 117 messagePattern = ENTRY_MESSAGE_ARRAY[argArray.length]; 118 } else { 119 messagePattern = buildMessagePattern(argArray.length); 120 } 121 FormattingTuple tp = MessageFormatter.arrayFormat(messagePattern, argArray); 122 ((LocationAwareLogger) logger).log(ENTRY_MARKER, FQCN, LocationAwareLogger.TRACE_INT, tp.getMessage(), argArray, tp.getThrowable()); 123 } 124 } 125 126 /** 127 * Log method exit 128 */ 129 public void exit() { 130 if (instanceofLAL && logger.isTraceEnabled(ENTRY_MARKER)) { 131 ((LocationAwareLogger) logger).log(EXIT_MARKER, FQCN, LocationAwareLogger.TRACE_INT, EXIT_MESSAGE_0, null, null); 132 } 133 } 134 135 /** 136 * Log method exit 137 * 138 * @param result The result of the method being exited 139 */ 140 public <T> T exit(T result) { 141 if (instanceofLAL && logger.isTraceEnabled(ENTRY_MARKER)) { 142 FormattingTuple tp = MessageFormatter.format(EXIT_MESSAGE_1, result); 143 ((LocationAwareLogger) logger).log(EXIT_MARKER, FQCN, LocationAwareLogger.TRACE_INT, tp.getMessage(), new Object[] { result }, tp.getThrowable()); 144 } 145 return result; 146 } 147 148 /** 149 * Log an exception being thrown. The generated log event uses Level ERROR. 150 * 151 * @param throwable the exception being caught. 152 */ 153 public <T extends Throwable> T throwing(T throwable) { 154 if (instanceofLAL) { 155 ((LocationAwareLogger) logger).log(THROWING_MARKER, FQCN, LocationAwareLogger.ERROR_INT, "throwing", null, throwable); 156 } 157 return throwable; 158 } 159 160 /** 161 * Log an exception being thrown allowing the log level to be specified. 162 * 163 * @param level the logging level to use. 164 * @param throwable the exception being caught. 165 */ 166 public <T extends Throwable> T throwing(Level level, T throwable) { 167 if (instanceofLAL) { 168 ((LocationAwareLogger) logger).log(THROWING_MARKER, FQCN, level.level, "throwing", null, throwable); 169 } 170 return throwable; 171 } 172 173 /** 174 * Log an exception being caught. The generated log event uses Level ERROR. 175 * 176 * @param throwable the exception being caught. 177 */ 178 public void catching(Throwable throwable) { 179 if (instanceofLAL) { 180 ((LocationAwareLogger) logger).log(CATCHING_MARKER, FQCN, LocationAwareLogger.ERROR_INT, "catching", null, throwable); 181 } 182 } 183 184 /** 185 * Log an exception being caught allowing the log level to be specified. 186 * 187 * @param level the logging level to use. 188 * @param throwable the exception being caught. 189 */ 190 public void catching(Level level, Throwable throwable) { 191 if (instanceofLAL) { 192 ((LocationAwareLogger) logger).log(CATCHING_MARKER, FQCN, level.level, "catching", null, throwable); 193 } 194 } 195 196 private static String buildMessagePattern(int len) { 197 StringBuilder sb = new StringBuilder(); 198 sb.append(" entry with ("); 199 for (int i = 0; i < len; i++) { 200 sb.append("{}"); 201 if (i != len - 1) 202 sb.append(", "); 203 } 204 sb.append(')'); 205 return sb.toString(); 206 } 207}