001 /*
002 * CDDL HEADER START
003 *
004 * The contents of this file are subject to the terms of the
005 * Common Development and Distribution License, Version 1.0 only
006 * (the "License"). You may not use this file except in compliance
007 * with the License.
008 *
009 * You can obtain a copy of the license at
010 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
012 * See the License for the specific language governing permissions
013 * and limitations under the License.
014 *
015 * When distributing Covered Code, include this CDDL HEADER in each
016 * file and include the License file at
017 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
018 * add the following below this CDDL HEADER, with the fields enclosed
019 * by brackets "[]" replaced with your own identifying information:
020 * Portions Copyright [yyyy] [name of copyright owner]
021 *
022 * CDDL HEADER END
023 *
024 *
025 * Copyright 2006-2008 Sun Microsystems, Inc.
026 */
027
028 package org.opends.server.loggers.debug;
029 import org.opends.messages.Message;
030
031 import org.opends.server.types.DebugLogLevel;
032 import org.opends.server.types.DebugLogCategory;
033 import org.opends.server.types.ConfigChangeResult;
034 import org.opends.server.types.ResultCode;
035 import org.opends.server.loggers.LogLevel;
036 import org.opends.server.loggers.LogCategory;
037 import org.opends.server.admin.server.ConfigurationChangeListener;
038 import org.opends.server.admin.std.meta.DebugTargetCfgDefn;
039 import org.opends.server.admin.std.server.DebugTargetCfg;
040
041
042 import java.util.Set;
043 import java.util.HashSet;
044 import java.util.ArrayList;
045 import java.util.List;
046
047 /**
048 * This class encapsulates the trace settings in effect at a given traceing
049 * scope.
050 */
051 public class TraceSettings
052 implements ConfigurationChangeListener<DebugTargetCfg>
053 {
054 /** A TraceSettings object representing a fully disabled trace state. */
055 public static final TraceSettings DISABLED =
056 new TraceSettings(DebugLogLevel.DISABLED);
057
058 private static final String STACK_DUMP_KEYWORD = "stack";
059 private static final String INCLUDE_CAUSE_KEYWORD = "cause";
060 private static final String SUPPRESS_ARG_KEYWORD = "noargs";
061 private static final String SUPPRESS_RETVAL_KEYWORD = "noretval";
062 private static final String INCLUDE_CATEGORY_KEYWORD = "category";
063 private static final String LEVEL_KEYWORD = "level";
064
065 /**
066 * The log level of this setting.
067 */
068 LogLevel level;
069
070 /**
071 * The log categories for this setting.
072 */
073 Set<LogCategory> includeCategories;
074
075 /**
076 * Indicates if method arguments should be logged.
077 */
078 boolean noArgs;
079
080 /**
081 * Indicates if method return values should be logged.
082 */
083 boolean noRetVal;
084
085 /**
086 * The level of stack frames to include.
087 */
088 int stackDepth;
089
090 /**
091 * Indicates if the cause exception is included in exception messages.
092 */
093 boolean includeCause;
094
095 private DebugTargetCfg currentConfig;
096
097 /**
098 * Construct new trace settings at the specified log level.
099 *
100 * @param level the log level for this setting.
101 */
102 public TraceSettings(LogLevel level)
103 {
104 this(level, null, false, false, 0, false);
105
106 }
107
108 /**
109 * Construct new trace settings at the specified log level and including
110 * the categories.
111 *
112 * @param level the log level for this setting.
113 * @param includeCategories the categories to include in this setting.
114 */
115 public TraceSettings(LogLevel level, Set<LogCategory> includeCategories)
116 {
117 this(level, includeCategories, false, false, 0, false);
118
119 }
120
121 /**
122 * Construct new trace settings at the specified log level and including
123 * the categories. Optionally turn off arguments and return value in entry
124 * and exit messages.
125 *
126 * @param level the log level for this setting.
127 * @param includeCategories the categories to include in this setting.
128 * @param noArgs whether to include arguments in the log messages.
129 * @param noRetVal whether to include return values in the log messages.
130 */
131 public TraceSettings(LogLevel level, Set<LogCategory> includeCategories,
132 boolean noArgs, boolean noRetVal)
133 {
134 this(level, includeCategories, noArgs, noRetVal, 0, false);
135 }
136
137 /**
138 * Construct new trace settings at the specified log level and including
139 * the categories. Optionally turn off arguments, return value in entry
140 * and exit messages, and specifying the depth of stack traces and whether
141 * to include the cause of exceptions.
142 *
143 * @param level the log level for this setting.
144 * @param includeCategories the categories to include in this setting.
145 * @param noArgs whether to include arguments in the log messages.
146 * @param noRetVal whether to include return values in the log messages.
147 * @param stackDepth the stack depth to display in log messages.
148 * @param includeCause whether to include the cause of exceptions.
149 */
150 public TraceSettings(LogLevel level, Set<LogCategory> includeCategories,
151 boolean noArgs, boolean noRetVal, int stackDepth,
152 boolean includeCause)
153 {
154 this.level = level;
155 this.includeCategories = includeCategories;
156 this.noArgs = noArgs;
157 this.noRetVal = noRetVal;
158 this.stackDepth = stackDepth;
159 this.includeCause = includeCause;
160 }
161
162 /**
163 * Construct a new trace settings from the provided configuration.
164 *
165 * @param config The debug target configuration that contains the information
166 * to use to initialize this trace setting.
167 */
168 public TraceSettings(DebugTargetCfg config)
169 {
170 this.level =
171 DebugLogLevel.parse(config.getDebugLevel().toString());
172
173 Set<LogCategory> logCategories = null;
174 if(!config.getDebugCategory().isEmpty())
175 {
176 logCategories =
177 new HashSet<LogCategory>(config.getDebugCategory().size());
178 for(DebugTargetCfgDefn.DebugCategory category :
179 config.getDebugCategory())
180 {
181 logCategories.add(DebugLogCategory.parse(category.toString()));
182 }
183 }
184
185 this.includeCategories = logCategories;
186 this.noArgs = config.isOmitMethodEntryArguments();
187 this.noRetVal = config.isOmitMethodReturnValue();
188 this.stackDepth = config.getThrowableStackFrames();
189 this.includeCause = config.isIncludeThrowableCause();
190
191 currentConfig = config;
192 config.addChangeListener(this);
193 }
194
195 /**
196 * {@inheritDoc}
197 */
198 public boolean isConfigurationChangeAcceptable(
199 DebugTargetCfg config,
200 List<Message> unacceptableReasons)
201 {
202 // This should alwas be acceptable. We are assuing that the scope for this
203 // trace setting is the same sine its part of the DN.
204 return true;
205 }
206
207 /**
208 * {@inheritDoc}
209 */
210 public ConfigChangeResult applyConfigurationChange(DebugTargetCfg config)
211 {
212 // Default result code.
213 ResultCode resultCode = ResultCode.SUCCESS;
214 boolean adminActionRequired = false;
215 ArrayList<Message> messages = new ArrayList<Message>();
216
217 // We can assume that the target scope did not change since its the
218 // naming attribute. Changing it would result in a modify DN.
219
220 this.level =
221 DebugLogLevel.parse(config.getDebugLevel().toString());
222
223 Set<LogCategory> logCategories = null;
224 if(!config.getDebugCategory().isEmpty())
225 {
226 logCategories =
227 new HashSet<LogCategory>(config.getDebugCategory().size());
228 for(DebugTargetCfgDefn.DebugCategory category :
229 config.getDebugCategory())
230 {
231 logCategories.add(DebugLogCategory.parse(category.toString()));
232 }
233 }
234
235 this.includeCategories = logCategories;
236 this.noArgs = config.isOmitMethodEntryArguments();
237 this.noRetVal = config.isOmitMethodReturnValue();
238 this.stackDepth = config.getThrowableStackFrames();
239 this.includeCause = config.isIncludeThrowableCause();
240
241 this.currentConfig = config;
242
243 return new ConfigChangeResult(resultCode, adminActionRequired, messages);
244 }
245
246 /**
247 * Parse trace settings from the string representation.
248 *
249 * @param value the trace settings string to be parsed.
250 * @return the trace settings parsed from the string.
251 */
252 protected static TraceSettings parseTraceSettings(String value)
253 {
254 TraceSettings settings = null;
255 if(value != null)
256 {
257 //Touch DebugLogLevel and DebugLogCategory so they are statically
258 //initialized or parse will not see all the levels/categories.
259 LogLevel level = DebugLogLevel.ERROR;
260 LogCategory categoryStub = DebugLogCategory.MESSAGE;
261
262 Set<LogCategory> includeCategories = null;
263 boolean noArgs = false;
264 boolean noRetVal = false;
265 int stackDepth = 0;
266 boolean includeCause = false;
267
268 String[] keywords = value.split(",");
269
270 for(String keyword : keywords)
271 {
272 //See if stack dump keyword is included
273 if(keyword.startsWith(STACK_DUMP_KEYWORD))
274 {
275 //See if a stack depth is included
276 if(keyword.length() == STACK_DUMP_KEYWORD.length())
277 {
278 stackDepth = DebugStackTraceFormatter.COMPLETE_STACK;
279 }
280 else
281 {
282 int depthStart= keyword.indexOf("=", STACK_DUMP_KEYWORD.length());
283 if (depthStart == STACK_DUMP_KEYWORD.length())
284 {
285 try
286 {
287 stackDepth = Integer.valueOf(keyword.substring(depthStart+1));
288 }
289 catch(NumberFormatException nfe)
290 { // TODO: i18n
291 System.err.println("The keyword " + STACK_DUMP_KEYWORD +
292 " contains an invalid depth value. The complete stack " +
293 "will be included.");
294 }
295 }
296 }
297 }
298 //See if to include cause in exception messages.
299 else if(keyword.equals(INCLUDE_CAUSE_KEYWORD))
300 {
301 includeCause = true;
302 }
303 //See if to supress method arguments.
304 else if(keyword.equals(SUPPRESS_ARG_KEYWORD))
305 {
306 noArgs = true;
307 }
308 //See if to supress return values.
309 else if(keyword.equals(SUPPRESS_RETVAL_KEYWORD))
310 {
311 noRetVal = true;
312 }
313 else if(keyword.startsWith(INCLUDE_CATEGORY_KEYWORD))
314 {
315 int categoryStart =
316 keyword.indexOf("=", INCLUDE_CATEGORY_KEYWORD.length());
317
318 if(keyword.length() == INCLUDE_CATEGORY_KEYWORD.length() ||
319 categoryStart != INCLUDE_CATEGORY_KEYWORD.length())
320 { // TODO: i18n
321 System.err.println("The keyword " + INCLUDE_CATEGORY_KEYWORD +
322 " does not contain an equal sign to define the set of " +
323 "categories to include. All categories will be included.");
324 }
325 else
326 {
327 String[] categories =
328 keyword.substring(categoryStart+1).split("[|]");
329 includeCategories = new HashSet<LogCategory>();
330 for(String category : categories)
331 {
332 try
333 {
334 includeCategories.add(DebugLogCategory.parse(category));
335 }
336 catch(IllegalArgumentException iae)
337 { // TODO: i18n
338 System.err.println("The keyword " + INCLUDE_CATEGORY_KEYWORD +
339 " contains an invalid debug log category: " +
340 iae.toString() + ". It will be ignored.");
341 }
342 }
343
344 }
345 }
346 else if(keyword.startsWith(LEVEL_KEYWORD))
347 {
348 int levelStart =
349 keyword.indexOf("=", LEVEL_KEYWORD.length());
350
351 if(keyword.length() == LEVEL_KEYWORD.length() ||
352 levelStart != LEVEL_KEYWORD.length())
353 { // TODO: i18n
354 System.err.println("The keyword " + LEVEL_KEYWORD +
355 " does not contain an equal sign to specify the log level. " +
356 "Default level of " + level.toString() + " will be used.");
357 }
358 else
359 {
360 try
361 {
362 level = LogLevel.parse(keyword.substring(levelStart+1));
363 }
364 catch(IllegalArgumentException iae)
365 { // TODO: i18n
366 System.err.println("The keyword " + LEVEL_KEYWORD +
367 " contains an invalid debug log level: " +
368 iae.toString() + ". Default level of " + level.toString() +
369 " will be used.");
370 }
371 }
372 }
373
374 }
375 settings = new TraceSettings(level, includeCategories, noArgs, noRetVal,
376 stackDepth, includeCause);
377 }
378
379 return settings;
380 }
381 }