1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.log4j.spi;
21
22 import java.io.StringWriter;
23 import java.io.PrintWriter;
24 import org.apache.log4j.helpers.LogLog;
25 import org.apache.log4j.Layout;
26
27 /***
28 The internal representation of caller location information.
29
30 @since 0.8.3
31 */
32 public class LocationInfo implements java.io.Serializable {
33
34 /***
35 Caller's line number.
36 */
37 transient String lineNumber;
38 /***
39 Caller's file name.
40 */
41 transient String fileName;
42 /***
43 Caller's fully qualified class name.
44 */
45 transient String className;
46 /***
47 Caller's method name.
48 */
49 transient String methodName;
50 /***
51 All available caller information, in the format
52 <code>fully.qualified.classname.of.caller.methodName(Filename.java:line)</code>
53 */
54 public String fullInfo;
55
56 private static StringWriter sw = new StringWriter();
57 private static PrintWriter pw = new PrintWriter(sw);
58
59 /***
60 When location information is not available the constant
61 <code>NA</code> is returned. Current value of this string
62 constant is <b>?</b>. */
63 public final static String NA = "?";
64
65 static final long serialVersionUID = -1325822038990805636L;
66
67 /***
68 * NA_LOCATION_INFO is provided for compatibility with log4j 1.3.
69 * @since 1.2.15
70 */
71 public static final LocationInfo NA_LOCATION_INFO =
72 new LocationInfo(NA, NA, NA, NA);
73
74
75
76
77 static boolean inVisualAge = false;
78 static {
79 try {
80 inVisualAge = Class.forName("com.ibm.uvm.tools.DebugSupport") != null;
81 LogLog.debug("Detected IBM VisualAge environment.");
82 } catch(Throwable e) {
83
84 }
85 }
86
87 /***
88 Instantiate location information based on a Throwable. We
89 expect the Throwable <code>t</code>, to be in the format
90
91 <pre>
92 java.lang.Throwable
93 ...
94 at org.apache.log4j.PatternLayout.format(PatternLayout.java:413)
95 at org.apache.log4j.FileAppender.doAppend(FileAppender.java:183)
96 at org.apache.log4j.Category.callAppenders(Category.java:131)
97 at org.apache.log4j.Category.log(Category.java:512)
98 at callers.fully.qualified.className.methodName(FileName.java:74)
99 ...
100 </pre>
101
102 <p>However, we can also deal with JIT compilers that "lose" the
103 location information, especially between the parentheses.
104
105 */
106 public LocationInfo(Throwable t, String fqnOfCallingClass) {
107 if(t == null || fqnOfCallingClass == null)
108 return;
109
110 String s;
111
112 synchronized(sw) {
113 t.printStackTrace(pw);
114 s = sw.toString();
115 sw.getBuffer().setLength(0);
116 }
117
118 int ibegin, iend;
119
120
121
122
123
124
125
126
127 ibegin = s.lastIndexOf(fqnOfCallingClass);
128 if(ibegin == -1)
129 return;
130
131
132 ibegin = s.indexOf(Layout.LINE_SEP, ibegin);
133 if(ibegin == -1)
134 return;
135 ibegin+= Layout.LINE_SEP_LEN;
136
137
138 iend = s.indexOf(Layout.LINE_SEP, ibegin);
139 if(iend == -1)
140 return;
141
142
143
144 if(!inVisualAge) {
145
146 ibegin = s.lastIndexOf("at ", iend);
147 if(ibegin == -1)
148 return;
149
150 ibegin += 3;
151 }
152
153 this.fullInfo = s.substring(ibegin, iend);
154 }
155
156 /***
157 * Appends a location fragment to a buffer to build the
158 * full location info.
159 * @param buf StringBuffer to receive content.
160 * @param fragment fragment of location (class, method, file, line),
161 * if null the value of NA will be appended.
162 * @since 1.2.15
163 */
164 private static final void appendFragment(final StringBuffer buf,
165 final String fragment) {
166 if (fragment == null) {
167 buf.append(NA);
168 } else {
169 buf.append(fragment);
170 }
171 }
172
173 /***
174 * Create new instance.
175 * @param file source file name
176 * @param classname class name
177 * @param method method
178 * @param line source line number
179 *
180 * @since 1.2.15
181 */
182 public LocationInfo(
183 final String file,
184 final String classname,
185 final String method,
186 final String line) {
187 this.fileName = file;
188 this.className = classname;
189 this.methodName = method;
190 this.lineNumber = line;
191 StringBuffer buf = new StringBuffer();
192 appendFragment(buf, classname);
193 buf.append(".");
194 appendFragment(buf, method);
195 buf.append("(");
196 appendFragment(buf, file);
197 buf.append(":");
198 appendFragment(buf, line);
199 buf.append(")");
200 this.fullInfo = buf.toString();
201 }
202
203 /***
204 Return the fully qualified class name of the caller making the
205 logging request.
206 */
207 public
208 String getClassName() {
209 if(fullInfo == null) return NA;
210 if(className == null) {
211
212
213 int iend = fullInfo.lastIndexOf('(');
214 if(iend == -1)
215 className = NA;
216 else {
217 iend =fullInfo.lastIndexOf('.', iend);
218
219
220
221
222
223
224
225
226
227
228 int ibegin = 0;
229 if (inVisualAge) {
230 ibegin = fullInfo.lastIndexOf(' ', iend)+1;
231 }
232
233 if(iend == -1)
234 className = NA;
235 else
236 className = this.fullInfo.substring(ibegin, iend);
237 }
238 }
239 return className;
240 }
241
242 /***
243 Return the file name of the caller.
244
245 <p>This information is not always available.
246 */
247 public
248 String getFileName() {
249 if(fullInfo == null) return NA;
250
251 if(fileName == null) {
252 int iend = fullInfo.lastIndexOf(':');
253 if(iend == -1)
254 fileName = NA;
255 else {
256 int ibegin = fullInfo.lastIndexOf('(', iend - 1);
257 fileName = this.fullInfo.substring(ibegin + 1, iend);
258 }
259 }
260 return fileName;
261 }
262
263 /***
264 Returns the line number of the caller.
265
266 <p>This information is not always available.
267 */
268 public
269 String getLineNumber() {
270 if(fullInfo == null) return NA;
271
272 if(lineNumber == null) {
273 int iend = fullInfo.lastIndexOf(')');
274 int ibegin = fullInfo.lastIndexOf(':', iend -1);
275 if(ibegin == -1)
276 lineNumber = NA;
277 else
278 lineNumber = this.fullInfo.substring(ibegin + 1, iend);
279 }
280 return lineNumber;
281 }
282
283 /***
284 Returns the method name of the caller.
285 */
286 public
287 String getMethodName() {
288 if(fullInfo == null) return NA;
289 if(methodName == null) {
290 int iend = fullInfo.lastIndexOf('(');
291 int ibegin = fullInfo.lastIndexOf('.', iend);
292 if(ibegin == -1)
293 methodName = NA;
294 else
295 methodName = this.fullInfo.substring(ibegin + 1, iend);
296 }
297 return methodName;
298 }
299 }