Source code: er/extensions/ERXPatternLayout.java
1 /*
2 * Copyright (C) NetStruxr, Inc. All rights reserved.
3 *
4 * This software is published under the terms of the NetStruxr
5 * Public Software License version 0.5, a copy of which has been
6 * included with this distribution in the LICENSE.NPL file. */
7 package er.extensions;
8
9 import org.apache.log4j.PatternLayout;
10 import org.apache.log4j.helpers.FormattingInfo;
11 import org.apache.log4j.helpers.PatternConverter;
12 import org.apache.log4j.helpers.PatternParser;
13 import org.apache.log4j.spi.LoggingEvent;
14 import java.util.Enumeration;
15 import com.webobjects.appserver.WOApplication;
16 import com.webobjects.appserver.WOAdaptor;
17 import com.webobjects.foundation.NSArray;
18 import com.webobjects.foundation.NSMutableArray;
19 import com.webobjects.foundation.NSDictionary;
20 import com.webobjects.foundation.NSMutableDictionary;
21
22 /**
23 * The ERXPatternLayout adds some additional (and needed) layout options. The
24 * first is by specifing an '@' character a full backtrace will be logged as part
25 * of the log event. The second is by specifing an '$' char the current application
26 * name of the WOApplication will be logged as part of the log event.
27 * Finally by specifing an '#' char the current port number on which the
28 * primary adaptor listens to will be logged as part of the log event.
29 *
30 * <pre>
31 * WebObjects Applicaion Info Patterns
32 * Example: %W{n[i:p s]} -- MyApp[9300:2001 28]
33 *
34 * n: application name
35 * i: pid (process ID, provided through Java system property "com.webobjects.pid")
36 * p: primary adaptor's port number
37 * s: active session count
38 *
39 * Java VM (Virtual Machine) Info Patterns
40 * Example: %V{u used/f free} -- 75.22 MB used/12.86 MB free
41 *
42 * t: total memory
43 * u: used memory
44 * f: free memory in the current heap size (not max)
45 *
46 * </pre>
47 *
48 */
49 // ENHANCEME: Need access to ERXThreadStorage, also need more WO stuff, could opt for a WO char
50 // and then specify all of the things to log as formatting info for that converter.
51 public class ERXPatternLayout extends PatternLayout {
52
53 /**
54 * Default constructor. Uses the default conversion
55 * pattern.
56 */
57 public ERXPatternLayout() {
58 this(DEFAULT_CONVERSION_PATTERN);
59 }
60
61 /**
62 * Default constructor. Uses the specified conversion
63 * pattern.
64 * @param pattern layout to be used.
65 */
66 public ERXPatternLayout(String pattern) {
67 super(pattern);
68 }
69
70 /**
71 * Creates a pattern parser for the given pattern.
72 * This method is called implicitly by the log4j
73 * logging system.
74 * @param pattern to create the pattern parser for
75 * @return an ERXPatternParser for the given pattern
76 */
77 public PatternParser createPatternParser(String pattern) {
78 return new ERXPatternParser(pattern == null ? DEFAULT_CONVERSION_PATTERN : pattern);
79 }
80 }
81
82 /**
83 * Pattern parser extension that adds support for WebObjects
84 * specific patterns.
85 */
86 class ERXPatternParser extends PatternParser {
87
88 /**
89 * Default constructor for a given pattern
90 * @param pattern to construct the parser for
91 */
92 public ERXPatternParser(String pattern) {
93 super(pattern);
94 }
95
96 /**
97 * Creates a converter for a particular
98 * character. This is the method that
99 * adds the custom converters for WO.
100 * @param c char to add the converter for
101 */
102 public void finalizeConverter(char c) {
103 switch (c) {
104 case '$':
105 addConverter(new AppNamePatternConverter(formattingInfo));
106 currentLiteral.setLength(0);
107 break;
108 case '#':
109 addConverter(new AdaptorPortNumberConverter(formattingInfo));
110 currentLiteral.setLength(0);
111 break;
112 case '@':
113 addConverter(new StackTracePatternConverter(formattingInfo));
114 currentLiteral.setLength(0);
115 break;
116 case 'W':
117 addConverter(new AppInfoPatternConverter(formattingInfo, extractOption()));
118 currentLiteral.setLength(0);
119 break;
120 case 'V':
121 addConverter(new JavaVMInfoPatternConverter(formattingInfo, extractOption()));
122 currentLiteral.setLength(0);
123 break;
124 default:
125 super.finalizeConverter(c);
126 break;
127 }
128 }
129
130 /**
131 * The stack trace pattern is useful for logging full stack traces
132 * when a log event occurs.
133 */
134 private class StackTracePatternConverter extends PatternConverter {
135 /**
136 * Package access level constructor.
137 * @param formattingInfo that is currently being used for this
138 * pattern converter.
139 */
140 StackTracePatternConverter(FormattingInfo formattingInfo) {
141 super(formattingInfo);
142 }
143
144 /**
145 * For a given log event returns the string representation
146 * of a stack trace for the current logging call minus all
147 * of the log4j stack.
148 * @param event current logging event
149 * @return string representation of the current backtrace.
150 */
151 public String convert(LoggingEvent event) {
152 NSArray parts = NSArray.componentsSeparatedByString(ERXUtilities.stackTrace(), "\n\t");
153 NSMutableArray subParts = new NSMutableArray();
154 boolean first = true;
155 for (Enumeration e = parts.reverseObjectEnumerator(); e.hasMoreElements();) {
156 String element = (String)e.nextElement();
157 if (element.indexOf("org.apache.log4j") != -1)
158 break;
159 if (!first)
160 subParts.insertObjectAtIndex(element, 0);
161 else
162 first = false;
163 }
164 return "\t" + subParts.componentsJoinedByString("\n\t") + "\n";
165 }
166 }
167
168 /**
169 * The application name pattern converter is useful for logging
170 * the current application name in log statements.
171 *
172 * @deprecated
173 */
174 private class AppNamePatternConverter extends PatternConverter {
175 /** holds a reference to the app name */
176 String _appName;
177
178 /**
179 * Default package level constructor
180 * @param formattingInfo current pattern formatting information
181 */
182 AppNamePatternConverter(FormattingInfo formattingInfo) {
183 super(formattingInfo);
184 }
185
186 /**
187 * Returns the current application name for the
188 * current logging event. If the application instance
189 * has not been created yet then "N/A" is logged.
190 * @param event a given logging event
191 * @return the current application name
192 */
193 public String convert(LoggingEvent event) {
194 if (_appName == null) {
195 if (WOApplication.application() != null)
196 _appName = WOApplication.application().name();
197 }
198 return _appName != null ? _appName : "N/A";
199 }
200 }
201
202 /**
203 * The adaptor port number pattern converter is useful for logging
204 * the current primary adaptor port in log statements.
205 *
206 * @deprecated
207 */
208 private class AdaptorPortNumberConverter extends PatternConverter {
209 /** holds a reference to the primary adaptor port */
210 String _portNumber;
211
212 /**
213 * Default package level constructor
214 * @param formattingInfo current pattern formatting information
215 */
216 AdaptorPortNumberConverter(FormattingInfo formattingInfo) {
217 super(formattingInfo);
218 }
219
220 /**
221 * Returns the current port number on which the
222 * primary adaptor listens to.
223 * This will be the same number specified
224 * by WOPort launch argument.
225 * <p>
226 * If the application or adapter instance
227 * has not been created yet then "N/A" is logged.
228 * @param event a given logging event
229 * @return the current application name
230 */
231 public String convert(LoggingEvent event) {
232 if (_portNumber == null) {
233 if (WOApplication.application() != null) {
234 //_portNumber = WOApplication.application().port().toString();
235
236 // WO 5.1.x -- Apple Ref# 2260519
237 NSArray adaptors = WOApplication.application().adaptors();
238 if (adaptors != null && adaptors.count() > 0) {
239 WOAdaptor primaryAdaptor = (WOAdaptor)adaptors.objectAtIndex(0);
240 _portNumber = String.valueOf(primaryAdaptor.port());
241 }
242 }
243 }
244 return _portNumber != null ? _portNumber : "N/A";
245 }
246 }
247
248 /**
249 * The <code>AppInfoPatternConverter</code> is useful for logging
250 * various info about the WebObjects applicaiton instance.
251 * See {@link ERXPatternLayout} for example/supported partterns.
252 */
253 private class AppInfoPatternConverter extends PatternConverter {
254
255 /** Template parser to format logging events */
256 private ERXSimpleTemplateParser _templateParser;
257
258 /** Template used by _templateParser */
259 private String _template;
260
261 /**
262 * Flag to indicate if the constant values are set.
263 * The constant values are the part of application info that
264 * shouldn't change during the application's life span.
265 */
266 private boolean _constantsInitialized = false;
267
268 /** Holds the values for the application info. Used by the template parser */
269 private NSMutableDictionary _appInfo;
270
271 /**
272 * Holds the default labels for the values.
273 * Note that the template parser will put "-" for undefined
274 * values by defauilt.
275 */
276 private final NSDictionary _defaultLabels =
277 ERXDictionaryUtilities.dictionaryWithObjectsAndKeys(new Object[] {"@sessionCount@", "sessionCount"});
278
279 /**
280 * Default package level constructor
281 *
282 * @param formattingInfo current pattern formatting information
283 * @param format string for the logging event format
284 */
285 // FIXME: Work in progress - fixed template; format parameter will be ignored for now.
286 AppInfoPatternConverter(FormattingInfo formattingInfo, String format) {
287 super(formattingInfo);
288 _templateParser = new ERXSimpleTemplateParser("-");
289 // This will prevent the convert method to get into an infinite loop
290 // when debug level logging is enabled for the perser.
291 _templateParser.isLoggingDisabled = true;
292 _appInfo = new NSMutableDictionary();
293 // work in progress; this is the fixed template.
294 _template = "@appName@[@pid@:@portNumber@ @sessionCount@]";
295 }
296
297 /**
298 * Returns ...
299 * <p>
300 *
301 * ... has not been created yet then "-" is logged.
302 *
303 * @param event a given logging event
304 * @return the current application name
305 */
306 public String convert(LoggingEvent event) {
307 WOApplication app = WOApplication.application();
308 if (app != null) {
309
310 if (! _constantsInitialized) {
311 String pid = System.getProperty("com.webobjects.pid");
312 if (pid != null)
313 _appInfo.setObjectForKey(pid, "pid");
314
315 String appName = app.name();
316 if (appName != null)
317 _appInfo.setObjectForKey(appName, "appName");
318
319 if (app.port() != null && app.port().intValue() > 0) {
320 _appInfo.setObjectForKey(app.port().toString(), "portNumber");
321 } else {
322 // WO 5.1.x -- Apple Ref# 2260519
323 NSArray adaptors = app.adaptors();
324 if (adaptors != null && adaptors.count() > 0) {
325 WOAdaptor primaryAdaptor = (WOAdaptor)adaptors.objectAtIndex(0);
326 String portNumber = String.valueOf(primaryAdaptor.port());
327 if (portNumber != null)
328 _appInfo.setObjectForKey(portNumber, "portNumber");
329 }
330 }
331
332 _template = _templateParser.parseTemplateWithObject(_template, "@", _appInfo, _defaultLabels);
333 _constantsInitialized = true;
334 }
335
336 _appInfo.setObjectForKey(String.valueOf(app.activeSessionsCount()), "sessionCount");
337 }
338 return _templateParser.parseTemplateWithObject(_template, "@", _appInfo);
339 }
340 }
341
342 /**
343 * The <code>JavaVMInfoPatternConverter</code> is useful for logging
344 * various info about the Java runtime and Virtual Machine that
345 * is running the application instance.
346 * See {@link ERXPatternLayout} for example/supported partterns.
347 */
348 private class JavaVMInfoPatternConverter extends PatternConverter {
349
350 /** */
351 private Runtime _runtime;
352
353 /** */
354 private ERXUnitAwareDecimalFormat _decimalFormatter;
355
356 /** Template parser to format logging events */
357 private ERXSimpleTemplateParser _templateParser;
358
359 /** Template used by _templateParser */
360 private String _template;
361
362 /**
363 * Flag to indicate if the constant values are set.
364 * The constant values are the part of application info that
365 * shouldn't change during the application's life span.
366 */
367 private boolean _constantsInitialized = false;
368
369 /** Holds the values for the JavaVM info. Used by the template parser */
370 private NSMutableDictionary _jvmInfo;
371
372 /**
373 * Holds the default labels for the values.
374 * Note that the template parser will put "-" for undefined
375 * values by defauilt.
376 */
377 private final NSDictionary _defaultLabels = null;
378
379 /**
380 * Default package level constructor
381 *
382 * @param formattingInfo current pattern formatting information
383 * @param format string for the logging event format
384 */
385 // FIXME: Work in progress - fixed template; format parameter will be ignored for now.
386 JavaVMInfoPatternConverter(FormattingInfo formattingInfo, String format) {
387 super(formattingInfo);
388 _runtime = Runtime.getRuntime();
389 _decimalFormatter = new ERXUnitAwareDecimalFormat(ERXUnitAwareDecimalFormat.BYTE);
390 _decimalFormatter.setMaximumFractionDigits(2);
391 _templateParser = new ERXSimpleTemplateParser("-");
392 // This will prevent the convert method to get into an infinite loop
393 // when debug level logging is enabled for the perser.
394 _templateParser.isLoggingDisabled = true;
395 _jvmInfo = new NSMutableDictionary();
396 // work in progress; this is the fixed template.
397 _template = "@usedMemory@ used/@freeMemory@ free";
398 }
399
400 /**
401 * Returns ...
402 * <p>
403 *
404 * ... has not been created yet then "-" is logged.
405 *
406 * @param event a given logging event
407 * @return the current application name
408 */
409 public String convert(LoggingEvent event) {
410 if (! _constantsInitialized) {
411 // Initialize constants here
412 _constantsInitialized = true;
413 }
414
415 long totalMemory = _runtime.totalMemory();
416 long freeMemory = _runtime.freeMemory();
417 long usedMemory = totalMemory - freeMemory;
418 _jvmInfo.setObjectForKey(_decimalFormatter.format(totalMemory), "totalMemory");
419 _jvmInfo.setObjectForKey(_decimalFormatter.format(freeMemory), "freeMemory" );
420 _jvmInfo.setObjectForKey(_decimalFormatter.format(usedMemory), "usedMemory" );
421
422 return _templateParser.parseTemplateWithObject(_template, "@", _jvmInfo);
423 }
424 }
425
426 }