View Javadoc
1   /*
2    * Copyright (C) 2007-2012 Argeo GmbH
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *         http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.argeo.slc.log4j;
17  
18  import java.util.Date;
19  import java.util.concurrent.BlockingQueue;
20  
21  import org.apache.log4j.AppenderSkeleton;
22  import org.apache.log4j.Level;
23  import org.apache.log4j.Logger;
24  import org.apache.log4j.spi.LoggingEvent;
25  import org.argeo.slc.core.execution.ExecutionThread;
26  import org.argeo.slc.core.execution.ProcessThreadGroup;
27  import org.argeo.slc.execution.ExecutionStep;
28  
29  /** Not meant to be used directly in standard log4j config */
30  public class SlcExecutionAppender extends AppenderSkeleton {
31  
32  	private Boolean disabled = false;
33  
34  	private String level = null;
35  
36  	private Level log4jLevel = null;
37  
38  	/** Marker to prevent stack overflow */
39  	private ThreadLocal<Boolean> dispatching = new ThreadLocal<Boolean>() {
40  
41  		@Override
42  		protected Boolean initialValue() {
43  			return false;
44  		}
45  	};
46  
47  	// private Layout layout = null;
48  	// private String pattern = "%m - %c%n";
49  	private Boolean onlyExecutionThread = false;
50  
51  	public void init() {
52  		// if (layout != null)
53  		// setLayout(layout);
54  		// else
55  		// setLayout(new PatternLayout(pattern));
56  		Logger.getRootLogger().addAppender(this);
57  	}
58  
59  	@Override
60  	protected void append(LoggingEvent event) {
61  		if (disabled)
62  			return;
63  
64  		if (dispatching.get())
65  			return;
66  
67  		if (level != null && !level.trim().equals("")) {
68  			if (log4jLevel == null || !log4jLevel.toString().equals(level))
69  				try {
70  					log4jLevel = Level.toLevel(level);
71  				} catch (Exception e) {
72  					System.err
73  							.println("Log4j level could not be set for level '"
74  									+ level + "', resetting it to null.");
75  					e.printStackTrace();
76  					level = null;
77  				}
78  
79  			if (log4jLevel != null
80  					&& !event.getLevel().isGreaterOrEqual(log4jLevel)) {
81  				return;
82  			}
83  		}
84  
85  		// Check whether we are within an executing process
86  		Thread currentThread = Thread.currentThread();
87  		if (currentThread.getThreadGroup() instanceof ProcessThreadGroup) {
88  			if (onlyExecutionThread
89  					&& !(currentThread instanceof ExecutionThread))
90  				return;
91  
92  			final String type;
93  			if (event.getLevel().equals(Level.ERROR)
94  					|| event.getLevel().equals(Level.FATAL))
95  				type = ExecutionStep.ERROR;
96  			else if (event.getLevel().equals(Level.WARN))
97  				type = ExecutionStep.WARNING;
98  			else if (event.getLevel().equals(Level.INFO))
99  				type = ExecutionStep.INFO;
100 			else if (event.getLevel().equals(Level.DEBUG))
101 				type = ExecutionStep.DEBUG;
102 			else if (event.getLevel().equals(Level.TRACE))
103 				type = ExecutionStep.TRACE;
104 			else
105 				type = ExecutionStep.INFO;
106 
107 			ExecutionStep step = new ExecutionStep(event.getLoggerName(),
108 					new Date(event.getTimeStamp()), type, event.getMessage()
109 							.toString());
110 
111 			try {
112 				dispatching.set(true);
113 				BlockingQueue<ExecutionStep> steps = ((ProcessThreadGroup) currentThread
114 						.getThreadGroup()).getSteps();
115 				if (steps.remainingCapacity() == 0) {
116 					stdOut("WARNING: execution steps queue is full, skipping step: "
117 							+ step);
118 					// FIXME understand why it block indefinitely: the queue
119 					// should be emptied by the logging thread
120 				} else {
121 					steps.add(step);
122 				}
123 			} finally {
124 				dispatching.set(false);
125 			}
126 		}
127 	}
128 
129 	public void destroy() throws Exception {
130 		Logger.getRootLogger().removeAppender(this);
131 	}
132 
133 	public void close() {
134 	}
135 
136 	public boolean requiresLayout() {
137 		return false;
138 	}
139 
140 	// public void setLayout(Layout layout) {
141 	// this.layout = layout;
142 	// }
143 
144 	/** For development purpose, since using regular logging is not easy here */
145 	static void stdOut(Object obj) {
146 		System.out.println(obj);
147 	}
148 
149 	// public void setPattern(String pattern) {
150 	// this.pattern = pattern;
151 	// }
152 
153 	public void setOnlyExecutionThread(Boolean onlyExecutionThread) {
154 		this.onlyExecutionThread = onlyExecutionThread;
155 	}
156 
157 	public void setDisabled(Boolean disabled) {
158 		this.disabled = disabled;
159 	}
160 
161 	public void setLevel(String level) {
162 		this.level = level;
163 	}
164 
165 }