View Javadoc
1   /*  This file is part of the aspect4log  project.
2    This program is free software; you can redistribute it and/or
3    modify it under the terms of the GNU Lesser General Public License
4    as published by the Free Software Foundation; version 2.1
5    of the License.
6    This program is distributed in the hope that it will be useful,
7    but WITHOUT ANY WARRANTY; without even the implied warranty of
8    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9    GNU Lesser General Public License for more details.
10   You should have received a copy of the GNU Lesser General Public License
11   along with this program; if not, write to the Free Software
12   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
13  
14   Copyright 2007-2014 Semochkin Vitaliy Evgenevich aka Yilativs
15    
16   */
17  package net.sf.aspect4log.aspect;
18  
19  import net.sf.aspect4log.Log;
20  import net.sf.aspect4log.Log.Level;
21  import net.sf.aspect4log.conf.LogFormatConfiguration;
22  import net.sf.aspect4log.conf.LogFormatConfigurationUtils;
23  import net.sf.aspect4log.text.MessageBuilder;
24  import net.sf.aspect4log.text.MessageBuilderFactory;
25  
26  import org.aspectj.lang.ProceedingJoinPoint;
27  import org.aspectj.lang.annotation.Around;
28  import org.aspectj.lang.annotation.Aspect;
29  import org.aspectj.lang.reflect.MethodSignature;
30  import org.slf4j.Logger;
31  import org.slf4j.LoggerFactory;
32  import org.slf4j.MDC;
33  
34  /**
35   * 
36   * An aspect to handle the logging rules defined via {@link Log}.
37   * 
38   * @author Vitaliy S <a href="mailto:vitaliy.se@gmail.com">
39   * 
40   */
41  @Aspect
42  public class LogAspect {
43  
44  	private static final LogAspect INSTANCE = new LogAspect();
45  
46  	/**
47  	 * @return instance of {@link LogAspect}
48  	 */
49  	public static LogAspect aspectOf() {
50  		return INSTANCE;
51  	}
52  
53  	/*
54  	 * LogFormatConfiguration can be only one per application
55  	 */
56  	private final static LogFormatConfiguration logFormatConfiguration = LogFormatConfigurationUtils.readConfiguration();
57  
58  	public static LogFormatConfiguration getLogFormatConfiguration() {
59  		return logFormatConfiguration;
60  	}
61  
62  
63  	/*
64  	 * it is marked static, because if there is more than one instance of LogApsect we must have one ident system per thread
65  	 */
66  	private static final ThreadLocal<Integer> thraedLocalIndent = new ThreadLocal<Integer>();
67  
68  	// @Around("execution(!@Log *(@Log *).*(..)) && @target(log)")
69  	@Around("(execution(!@net.sf.aspect4log.Log *(@net.sf.aspect4log.Log *).*(..))|| execution(!@net.sf.aspect4log.Log *.new(..)))  && @within(log)")
70  	public Object logNotAnnotatedMethondsInAnnotatedClasses(ProceedingJoinPoint pjp, Log log) throws Throwable {
71  		return log(pjp, log);
72  	}
73  
74  	// @Around("@annotation(log)")
75  	@Around("(execution(@net.sf.aspect4log.Log *.new(..)) || execution(@net.sf.aspect4log.Log * *.*(..)) ) && @annotation(log)")
76  	public Object logAnnotatedMethods(ProceedingJoinPoint pjp, Log log) throws Throwable {
77  		return log(pjp, log);
78  	}
79  
80  	public static final Integer getThreadLocalIdent() {
81  		return thraedLocalIndent.get() == null ? Integer.valueOf(0) : thraedLocalIndent.get();
82  	}
83  
84  	private Object log(ProceedingJoinPoint pjp, Log log) throws Throwable {
85  
86  		MessageBuilderFactory factory = logFormatConfiguration.getMessageBuilderFactory();
87  
88  		boolean isConstractorCall = "constructor-execution".equals(pjp.getStaticPart().getKind());
89  
90  		String declaringClass = pjp.getSignature().getDeclaringTypeName();
91  		String methodName = isConstractorCall ? pjp.getSignature().getDeclaringType().getSimpleName() : pjp.getSignature().getName();
92  		Logger logger = LoggerFactory.getLogger(declaringClass);
93  		try {
94  			increaseIndent(log);
95  			setMDC(log, pjp.getArgs());
96  
97  			MessageBuilder enterMessageBuilder = factory.createEnterMessageBuilder(methodName, log, pjp.getArgs());
98  			log(logger, log.enterLevel(), enterMessageBuilder, null);
99  			Object result = pjp.proceed();
100 			Class<?> returnClass = null;
101 			boolean returnsNothing = true;
102 			if (isConstractorCall) {
103 				returnsNothing = false;
104 			} else {
105 				returnClass = ((MethodSignature) pjp.getSignature()).getReturnType();
106 				returnsNothing = Void.TYPE.equals(returnClass);
107 			}
108 			MessageBuilder messageBuilder = factory.createSuccessfulReturnMessageBuilder(methodName, log, pjp.getArgs(), returnsNothing, result);
109 			log(logger, log.exitLevel(), messageBuilder, null);
110 			return result;
111 		} catch (Throwable e) {
112 			Level level = Level.ERROR;
113 			Throwable throwable = e;
114 			String template = Log.Exceptions.EXCEPTION_DEFAULT_TEMPLATE;
115 			exceptionExitSearchLoop: for (Log.Exceptions exceptions : log.on()) {
116 				for (Class<? extends Throwable> t : exceptions.exceptions()) {
117 					if (t.isAssignableFrom(e.getClass())) {
118 						level = exceptions.level();
119 						throwable = exceptions.stackTrace() ? e : null;
120 						template = exceptions.template();
121 						break exceptionExitSearchLoop;
122 					}
123 				}
124 			}
125 			MessageBuilder messageBuilder = factory.createExceptionReturnMessageBuilder(methodName, log, pjp.getArgs(), e, template);
126 			log(logger, level, messageBuilder, throwable);
127 			throw e;
128 		} finally {
129 			decreaseIndent(log);
130 			removeMDC(log);
131 		}
132 	}
133 
134 	private void setMDC(Log log, Object[] args) {
135 		if (!log.mdcKey().isEmpty()) {
136 			MessageBuilder mdcMessageBuilder = logFormatConfiguration.getMessageBuilderFactory().createMdcTemplate(log, args);
137 			MDC.put(log.mdcKey(), mdcMessageBuilder.build());
138 		}
139 	}
140 
141 	private void removeMDC(Log log) {
142 		if (!log.mdcKey().isEmpty()) {
143 			MDC.remove(log.mdcKey());
144 		}
145 	}
146 
147 	private void increaseIndent(Log log) {
148 		if (log.indent()) {
149 			if (thraedLocalIndent.get() == null) {
150 				thraedLocalIndent.set(Integer.valueOf(0));
151 			} else if (thraedLocalIndent.get() != null) {
152 				thraedLocalIndent.set(Integer.valueOf(thraedLocalIndent.get() + 1));
153 			}
154 		}
155 	}
156 
157 	private void decreaseIndent(Log log) {
158 		if (log.indent()) {
159 			if (thraedLocalIndent.get().equals(Integer.valueOf(0))) {
160 				thraedLocalIndent.remove();
161 			} else {
162 				thraedLocalIndent.set(Integer.valueOf(thraedLocalIndent.get() - 1));
163 			}
164 		}
165 	}
166 
167 	private void log(Logger logger, Level level, MessageBuilder messageBuilder, Throwable throable) {
168 		switch (level) {
169 		case TRACE:
170 			if (logger.isTraceEnabled()) {
171 				logger.trace(messageBuilder.build(), throable);
172 			}
173 			break;
174 		case DEBUG:
175 			if (logger.isDebugEnabled()) {
176 				logger.debug(messageBuilder.build(), throable);
177 			}
178 			break;
179 		case INFO:
180 			if (logger.isInfoEnabled()) {
181 				logger.info(messageBuilder.build(), throable);
182 			}
183 			break;
184 		case WARN:
185 			if (logger.isWarnEnabled()) {
186 				logger.warn(messageBuilder.build(), throable);
187 			}
188 			break;
189 		case ERROR:
190 			if (logger.isErrorEnabled()) {
191 				logger.error(messageBuilder.build(), throable);
192 			}
193 			break;
194 		default:
195 			//NONE level do not print any log
196 		}
197 	}
198 
199 }