Last sync 2016.04.01
[deliverable/titan.core.git] / titan_executor_api / TITAN_Executor_API / src / org / eclipse / titan / executorapi / util / Log.java
CommitLineData
970ed795 1/******************************************************************************
d44e3c4f 2 * Copyright (c) 2000-2016 Ericsson Telecom AB
970ed795
EL
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
d44e3c4f 7 *
8 * Contributors:
9 * Balasko, Jeno
10 * Lovassy, Arpad
11 *
970ed795
EL
12 ******************************************************************************/
13package org.eclipse.titan.executorapi.util;
14
15import java.text.SimpleDateFormat;
16import java.util.Date;
17import java.util.HashMap;
18import java.util.Map;
19
20/**
21 * Logger utility class.
22 * <p>
23 * Example usage:
24 * <pre>
25 * class A {
26 *
27 * void f1() {
28 * Log.fi();
29 * f2("abc");
30 * Log.fo();
31 * }
32 *
33 * void f2( String s ) {
34 * Log.fi(s);
35 * Log.f("Call doSomething()");
36 * doSomeThing();
37 * Log.fo();
38 * }
39 *
40 * ...
41 * }
42 * </pre>
43 *
44 * <p>
45 * Output:
46 * <pre>
47 * 2014-04-24 15:36:51.203 1 -> A.f1()
48 * 2014-04-24 15:36:51.203 1 -> A.f2( "abc" )
49 * 2014-04-24 15:36:51.203 1 Call doSomething()
50 * 2014-04-24 15:36:51.203 1 <- A.f2()
51 * 2014-04-24 15:36:51.204 1 <- A.f1()
52 * </pre>
53 */
54public class Log {
55
56 /**
57 * Severity of the log message, log level
58 * @see "http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/Level.html"
59 */
60 private enum Severity {
61 /** The OFF has the highest possible rank and is intended to turn off logging. */
62 OFF,
63
64 /** The FATAL level designates very severe error events that will presumably lead the application to abort. */
65 FATAL,
66
67 /** The ERROR level designates error events that might still allow the application to continue running. */
68 ERROR,
69
70 /** The WARN level designates potentially harmful situations. */
71 WARN,
72
73 /** The INFO level designates informational messages that highlight the progress of the application at coarse-grained level. */
74 INFO,
75
76 /** The DEBUG Level designates fine-grained informational events that are most useful to debug an application. */
77 DEBUG,
78
79 /** The TRACE Level designates finer-grained informational events than the DEBUG */
80 TRACE,
81
82 /** The ALL has the lowest possible rank and is intended to turn on all logging. */
83 ALL;
84 };
85
86 /**
87 * Global log level.
88 * It effects all the projects that use Log.
89 */
90 private static Severity sLogLevel = Severity.OFF;
91
92 /**
93 * Date format for function related logging functions: fi(), fo(), f()
94 */
95 private static final SimpleDateFormat sFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
96
97 /**
98 * Keeps track the call level for each thread.
99 * Call level is a number, that is increased, when a function entry point is logged (fi() is called),
100 * decreased, when a function exit point is logged (fo() is called).
101 * The indentation of the log message depends on the call level, higher call level means more indentation.
102 * Negative call level is treated as 0.
103 */
104 private static Map<Long, Integer> sCallLevels = new HashMap<Long, Integer>();
105
106 /**
107 * Logging states separately for each thread.
108 * Logging can be switched off separately for each thread. true: on, false: off. Default value: true.
109 */
110 private static Map<Long, Boolean> sLoggingStates = new HashMap<Long, Boolean>();
111
112 /**
113 * Function in.
114 * Logs the entry and the given parameter(s) of the method in DEBUG level.
115 * It must be placed at the entry point(s) of the method.
116 * @param aParams array of logged function parameters, it must be added one-by-one,
117 * it doesn't be resolved automatically like class and method name
118 */
119 public static void fi( final Object... aParams ) {
120 if ( !checkLogLevel( Severity.DEBUG ) || isThreadLoggingOff() ) {
121 return;
122 }
123
124 StringBuilder sb = new StringBuilder();
125 logDateAndThreadId(sb);
126 sb.append(" -> ");
127
128 final int callLevel = getCallLevel();
129 setCallLevel( callLevel + 1 );
130 for (int i = 0; i < callLevel; i++) {
131 sb.append(" ");
132 }
133
134 // 0: getStackTrace(), 1: logMethodName(), 2: fi(), 3: this is the caller function we want to log
135 logMethodName( sb, 3 );
136
137 // log parameters
138 sb.append("(");
139 if ( aParams.length > 0 ) {
140 sb.append(" ");
141 for ( int i = 0; i < aParams.length; i++ ) {
142 if ( i > 0 ) {
143 sb.append(", ");
144 }
145 StringUtil.appendObject(sb, aParams[i]);
146 }
147 sb.append(" ");
148 }
149 sb.append(")");
150
151 printLog( sb );
152 }
153
154 /**
155 * Function out.
156 * Logs the exit and the given return value of the method in DEBUG level.
157 * It must be placed at the exit point(s) of the method.
158 * @param aHasReturnValue true to log return value
159 * @param aReturnValue the return value to log, it can be null if aHasReturnValue == false
160 */
161 private static void fo( final boolean aHasReturnValue, final Object aReturnValue ) {
162 if ( !checkLogLevel( Severity.DEBUG ) || isThreadLoggingOff() ) {
163 return;
164 }
165
166 StringBuilder sb = new StringBuilder();
167 logDateAndThreadId(sb);
168 sb.append( " <- " );
169
170 int callLevel = getCallLevel();
171 setCallLevel( --callLevel );
172 for (int i = 0; i < callLevel; i++) {
173 sb.append(" ");
174 }
175
176 // 0: getStackTrace(), 1: logMethodName(), 2: fo (this private), 3: fo (one of the public ones), 4: this is caller the function we want to log
177 logMethodName( sb, 4 );
178
179 // log return value (if any)
180 sb.append("()");
181 if ( aHasReturnValue ) {
182 sb.append(" ");
183 StringUtil.appendObject(sb, aReturnValue);
184 }
185 printLog( sb );
186 }
187
188 /**
189 * Function out.
190 * Logs the exit of the method (but not the return value) in DEBUG level.
191 * It must be placed at the exit point(s) of the method.
192 */
193 public static void fo() {
194 fo( false, null );
195 }
196
197 /**
198 * Function out.
199 * Logs the exit and the given return value of the method in DEBUG level.
200 * It must be placed at the exit point(s) of the method.
201 * @param aReturnValue return value to log
202 */
203 public static void fo( final Object aReturnValue ) {
204 fo( true, aReturnValue );
205 }
206
207 /**
208 * function log (just a general log within the function)
209 * @param aMsg log message
210 */
211 public static void f( final String aMsg ) {
212 if ( !checkLogLevel( Severity.TRACE ) || isThreadLoggingOff() ) {
213 return;
214 }
215
216 StringBuilder sb = new StringBuilder();
217 logDateAndThreadId(sb);
218 sb.append( " " );
219
220 final int callLevel = getCallLevel();
221 for (int i = 0; i < callLevel; i++) {
222 sb.append(" ");
223 }
224
225 if ( aMsg != null && aMsg.contains( "\n" ) ) {
226 // in case of multiline message other lines are also indented with the same number of spaces
227 final int len = sb.length();
228 StringBuilder sbMsg = new StringBuilder( aMsg );
229 //replacement instead of \n
230 StringBuilder sbSpaces = new StringBuilder( len );
231 sbSpaces.append("\n");
232 for (int i = 0; i < len; i++) {
233 sbSpaces.append(" ");
234 }
235
236 // replace \n -> sbSpaces in sbMsg
237 StringUtil.replaceString( sbMsg, "\n", sbSpaces.toString() );
238 sb.append( sbMsg );
239 }
240 else {
241 sb.append( aMsg );
242 }
243
244 printLog( sb );
245 }
246
247 /**
248 * info
249 * @param aMsg log message
250 */
251 public static void i( final String aMsg ) {
252 if ( !checkLogLevel( Severity.INFO ) || isThreadLoggingOff() ) {
253 return;
254 }
255 StringBuilder sb = new StringBuilder();
256 logDateAndThreadId(sb);
257 sb.append( " " + aMsg );
258 printLog( sb );
259 }
260
261 /**
262 * info unformatted (without datetime)
263 * @param aMsg log message
264 */
265 public static void iu( final String aMsg ) {
266 if ( !checkLogLevel( Severity.INFO ) || isThreadLoggingOff() ) {
267 return;
268 }
269 StringBuilder sb = new StringBuilder( aMsg );
270 printLog( sb );
271 }
272
273 /**
274 * Switch on logging for the current thread
275 */
276 public static void on() {
277 final long threadId = Thread.currentThread().getId();
278 sLoggingStates.put(threadId, true);
279 }
280
281 /**
282 * Switch off logging for the current thread
283 */
284 public static void off() {
285 final long threadId = Thread.currentThread().getId();
286 sLoggingStates.put(threadId, false);
287 }
288
289 /**
290 * Adds full datetime and thread id to the log string.
291 * @param aSb [in/out] the log string, where new strings are added
292 */
293 private static void logDateAndThreadId( final StringBuilder aSb ) {
294 final long threadId = Thread.currentThread().getId();
295 aSb.append( sFormat.format( new Date() ) + " " + String.format( "%3d", threadId ) );
296 }
297
298 /**
299 * Checks if the global static log level reaches the minimum required log level,
300 * @param aMinRequired minimum required log level
301 * @return true, if the global log level >= required log level
302 */
303 private static boolean checkLogLevel( final Severity aMinRequired ) {
304 return sLogLevel.ordinal() >= aMinRequired.ordinal();
305 }
306
307 /**
308 * Adds class and method name to the log string.
309 * Short class name is used without full qualification.
310 * @param aSb [in/out] the log string, where new strings are added
311 * @param aStackTraceElementIndex [in] the call stack index, where we get the class and method name.
312 * + 1 must be added, because logMethodName() increases the call stack size by 1
313 */
314 private static void logMethodName( final StringBuilder aSb, final int aStackTraceElementIndex ) {
315 final StackTraceElement ste = Thread.currentThread().getStackTrace()[ aStackTraceElementIndex ];
316 final String className = ste.getClassName();
317 final String shortClassName = className.substring( className.lastIndexOf('.') + 1 );
318 final String methodName = ste.getMethodName();
319 aSb.append( shortClassName + "." + methodName );
320 }
321
322 /**
323 * @return true if logging for the current thread switched off.
324 * If there is no info for this thread, a new (<current thread>, true) item is added to the map
325 */
326 private static boolean isThreadLoggingOff() {
327 final long threadId = Thread.currentThread().getId();
328
329 if(!sLoggingStates.containsKey(threadId)) {
330 sLoggingStates.put(threadId, true);
331 }
332
333 return !sLoggingStates.get( threadId );
334 }
335
336 /**
337 * @return Call level for the current thread.
338 * If there is no info for this thread, a new (<current thread>, 0) item is added to the map
339 */
340 private static int getCallLevel() {
341 final long threadId = Thread.currentThread().getId();
342 if(!sCallLevels.containsKey( threadId ) ) {
343 sCallLevels.put( threadId, 0 );
344 }
345 final int callLevel = sCallLevels.get( threadId );
346 return callLevel;
347 }
348
349 /**
350 * Sets the call level of the current thread.
351 * If sCallLevels does NOT contain current thread as key, it creates it.
352 * @param aNewValue new value of the logging level for the thread, in can be negative
353 */
354 private static void setCallLevel( final int aNewValue ) {
355 final long threadId = Thread.currentThread().getId();
356 sCallLevels.put( threadId, aNewValue );
357 }
358
359 /**
360 * prints the log string to the output
361 * @param aLogString the log string, it can be multiline
362 */
363 private static void printLog( final StringBuilder aLogString ) {
364 System.out.println( aLogString );
365 }
366}
This page took 0.037585 seconds and 5 git commands to generate.