001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions Copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2006-2009 Sun Microsystems, Inc. 015 * Portions Copyright 2014-2016 ForgeRock AS. 016 */ 017package org.opends.server.loggers; 018 019import java.util.List; 020 021import org.forgerock.i18n.LocalizableMessage; 022import org.forgerock.opendj.config.server.ConfigChangeResult; 023import org.forgerock.opendj.config.server.ConfigurationChangeListener; 024import org.forgerock.opendj.server.config.server.DebugTargetCfg; 025 026/** This class encapsulates the trace settings in effect at a given tracing scope. */ 027public class TraceSettings implements 028 ConfigurationChangeListener<DebugTargetCfg> 029{ 030 /** A TraceSettings object representing a fully disabled trace state. */ 031 public static final TraceSettings DISABLED = new TraceSettings(Level.DISABLED); 032 033 private static final String STACK_DUMP_KEYWORD = "stack"; 034 private static final String INCLUDE_CAUSE_KEYWORD = "cause"; 035 private static final String SUPPRESS_ARG_KEYWORD = "noargs"; 036 private static final String SUPPRESS_RETVAL_KEYWORD = "noretval"; 037 private static final String ENABLED_KEYWORD = "enabled"; 038 private static final String EXCEPTIONS_ONLY_KEYWORD = "exceptionsonly"; 039 040 /** Represents the level of trace. */ 041 enum Level 042 { 043 /** Log nothing. */ 044 DISABLED, 045 /** Log only exceptions. */ 046 EXCEPTIONS_ONLY, 047 /** Log everything. */ 048 ALL; 049 050 /** 051 * Returns the level corresponding to provided options. 052 * 053 * @param isEnabled 054 * Indicates if tracer is enabled. 055 * @param isDebugExceptionsOnly 056 * Indicates if tracer should log only exceptions. 057 * @return the level corresponding to options 058 */ 059 static Level getLevel(boolean isEnabled, boolean isDebugExceptionsOnly) 060 { 061 if (isEnabled) 062 { 063 if (isDebugExceptionsOnly) 064 { 065 return Level.EXCEPTIONS_ONLY; 066 } 067 return Level.ALL; 068 } 069 return Level.DISABLED; 070 } 071 } 072 073 /** The level of this setting. */ 074 private Level level; 075 /** Indicates if method arguments should be logged. */ 076 private boolean noArgs; 077 /** Indicates if method return values should be logged. */ 078 private boolean noRetVal; 079 /** The level of stack frames to include. */ 080 private int stackDepth; 081 /** Indicates if the cause exception is included in exception messages. */ 082 private boolean includeCause; 083 084 /** Construct new trace settings with default values. */ 085 public TraceSettings() 086 { 087 this(Level.ALL, false, false, 0, false); 088 } 089 090 /** 091 * Construct new trace settings at provided level. 092 * 093 * @param level 094 * Level for this settings. 095 */ 096 private TraceSettings(Level level) 097 { 098 this(level, false, false, 0, false); 099 } 100 101 /** 102 * Construct new trace settings at the specified level. Optionally turn off 103 * arguments, return value in entry and exit messages, and specifying the 104 * depth of stack traces and whether to include the cause of exceptions. 105 * 106 * @param level 107 * the level for this setting. 108 * @param noArgs 109 * whether to include arguments in the log messages. 110 * @param noRetVal 111 * whether to include return values in the log messages. 112 * @param stackDepth 113 * the stack depth to display in log messages. 114 * @param includeCause 115 * whether to include the cause of exceptions. 116 */ 117 TraceSettings(Level level, boolean noArgs, 118 boolean noRetVal, int stackDepth, boolean includeCause) 119 { 120 this.level = level; 121 this.noArgs = noArgs; 122 this.noRetVal = noRetVal; 123 this.stackDepth = stackDepth; 124 this.includeCause = includeCause; 125 } 126 127 /** 128 * Construct a new trace settings from the provided configuration. 129 * 130 * @param config 131 * The debug target configuration that contains the information to 132 * use to initialize this trace setting. 133 */ 134 TraceSettings(DebugTargetCfg config) 135 { 136 this.level = Level.getLevel(config.isEnabled(), config.isDebugExceptionsOnly()); 137 this.noArgs = config.isOmitMethodEntryArguments(); 138 this.noRetVal = config.isOmitMethodReturnValue(); 139 this.stackDepth = config.getThrowableStackFrames(); 140 this.includeCause = config.isIncludeThrowableCause(); 141 142 config.addChangeListener(this); 143 } 144 145 @Override 146 public boolean isConfigurationChangeAcceptable(DebugTargetCfg config, 147 List<LocalizableMessage> unacceptableReasons) 148 { 149 // This should always be acceptable. We are assuming that the scope for this 150 // trace setting is the same since it is part of the DN. 151 return true; 152 } 153 154 @Override 155 public ConfigChangeResult applyConfigurationChange(DebugTargetCfg config) 156 { 157 final ConfigChangeResult ccr = new ConfigChangeResult(); 158 // We can assume that the target scope did not change since it is the 159 // naming attribute. Changing it would result in a modify DN. 160 161 this.level = Level.getLevel(config.isEnabled(), config.isDebugExceptionsOnly()); 162 this.noArgs = config.isOmitMethodEntryArguments(); 163 this.noRetVal = config.isOmitMethodReturnValue(); 164 this.stackDepth = config.getThrowableStackFrames(); 165 this.includeCause = config.isIncludeThrowableCause(); 166 167 return ccr; 168 } 169 170 /** 171 * Parse trace settings from the string representation. 172 * 173 * @param value 174 * the trace settings string to be parsed. 175 * @return the trace settings parsed from the string. 176 */ 177 protected static TraceSettings parseTraceSettings(String value) 178 { 179 TraceSettings settings = null; 180 if (value != null) 181 { 182 boolean enabled = false; 183 boolean exceptionsOnly = false; 184 boolean noArgs = false; 185 boolean noRetVal = false; 186 int stackDepth = 0; 187 boolean includeCause = false; 188 189 String[] keywords = value.split(","); 190 191 for (String keyword : keywords) 192 { 193 //See if stack dump keyword is included 194 if (keyword.startsWith(STACK_DUMP_KEYWORD)) 195 { 196 //See if a stack depth is included 197 if (keyword.length() == STACK_DUMP_KEYWORD.length()) 198 { 199 stackDepth = DebugStackTraceFormatter.COMPLETE_STACK; 200 } 201 else 202 { 203 int depthStart = keyword.indexOf("=", STACK_DUMP_KEYWORD.length()); 204 if (depthStart == STACK_DUMP_KEYWORD.length()) 205 { 206 try 207 { 208 stackDepth = Integer.valueOf(keyword.substring(depthStart + 1)); 209 } 210 catch (NumberFormatException nfe) 211 { // TODO: i18n 212 System.err.println("The keyword " + STACK_DUMP_KEYWORD 213 + " contains an invalid depth value. The complete stack " 214 + "will be included."); 215 } 216 } 217 } 218 } 219 //See if to include cause in exception messages. 220 else if (INCLUDE_CAUSE_KEYWORD.equals(keyword)) 221 { 222 includeCause = true; 223 } 224 // See if to suppress method arguments. 225 else if (SUPPRESS_ARG_KEYWORD.equals(keyword)) 226 { 227 noArgs = true; 228 } 229 // See if to suppress return values. 230 else if (SUPPRESS_RETVAL_KEYWORD.equals(keyword)) 231 { 232 noRetVal = true; 233 } 234 else if (ENABLED_KEYWORD.equals(keyword)) 235 { 236 enabled = true; 237 } 238 else if (EXCEPTIONS_ONLY_KEYWORD.equals(keyword)) 239 { 240 exceptionsOnly = true; 241 } 242 } 243 settings = 244 new TraceSettings(Level.getLevel(enabled, exceptionsOnly), 245 noArgs, noRetVal, stackDepth, includeCause); 246 } 247 248 return settings; 249 } 250 251 /** 252 * Get the level of this setting. 253 * 254 * @return the level of this setting. 255 */ 256 public Level getLevel() 257 { 258 return level; 259 } 260 261 /** 262 * Get whether method arguments should be logged. 263 * 264 * @return if method arguments should be logged. 265 */ 266 public boolean isNoArgs() 267 { 268 return noArgs; 269 } 270 271 /** 272 * Get whether method return values should be logged. 273 * 274 * @return if method return values should be logged. 275 */ 276 public boolean isNoRetVal() 277 { 278 return noRetVal; 279 } 280 281 /** 282 * Get the level of stack frames to include. 283 * 284 * @return the level of stack frames to include. 285 */ 286 public int getStackDepth() 287 { 288 return stackDepth; 289 } 290 291 /** 292 * Get whether the cause exception is included in exception messages. 293 * 294 * @return if the cause exception is included in exception messages. 295 */ 296 public boolean isIncludeCause() 297 { 298 return includeCause; 299 } 300 301 @Override 302 public String toString() 303 { 304 return getClass().getSimpleName() + "(" 305 + "level=" + level 306 + ", logMethodArguments=" + !noArgs 307 + ", logMethodReturnValue=" + !noRetVal 308 + ", logCauseException=" + includeCause 309 + ", stackDepth=" + stackDepth 310 + ")"; 311 } 312}