001/*
002 * Copyright (c) 2007-2017 Xplenty, Inc. All Rights Reserved.
003 *
004 * Project and contact information: http://www.cascading.org/
005 *
006 * This file is part of the Cascading project.
007 *
008 * Licensed under the Apache License, Version 2.0 (the "License");
009 * you may not use this file except in compliance with the License.
010 * You may obtain a copy of the License at
011 *
012 *     http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing, software
015 * distributed under the License is distributed on an "AS IS" BASIS,
016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017 * See the License for the specific language governing permissions and
018 * limitations under the License.
019 */
020
021package cascading.tap;
022
023import java.util.Properties;
024
025import cascading.property.Props;
026
027/**
028 * Class TrapProps is a fluent helper class to set properties which control the behaviour of {@link cascading.tap.Tap}
029 * instances used as traps on a given {@link cascading.flow.Flow}.
030 * <p/>
031 * A Tap trap is used to capture bad data that has triggered an unhandled {@link java.lang.Throwable} within a
032 * Cascading {@link cascading.operation.Operation} or {@link cascading.tap.Tap} (either as a source or sink).
033 * <p/>
034 * When a trap captures a failure, the {@link cascading.tuple.Tuple} arguments to the Operation or Tap will be
035 * captured and the Flow will continue executing with the next Tuple in the stream. Otherwise a Flow would
036 * typically fail.
037 * <p/>
038 * Due to the variability of data in the stream at any given point, a Tap trap should be configured with a
039 * {@link cascading.scheme.Scheme} that sinks {@link cascading.tuple.Fields#ALL}, or is guaranteed to sink known
040 * fields common to all Operations within the branch the trap has been bound too.
041 * <p/>
042 * Optionally diagnostic information, with the given field names, may be captured along with the argument Tuple.
043 * <p/>
044 * <ul>
045 * <li>{@code element-trace} - the file and line number the failed operation was instantiated</li>
046 * <li>{@code throwable-message} - the {@link Throwable#getMessage()} value</li>
047 * <li>{@code throwable-stacktrace} - the {@link Throwable#printStackTrace()} value, cleansed</li>
048 * </ul>
049 * <p/>
050 * By default, if the Throwable stacktrace is captured, each line of the trace will be trimmed (to remove the
051 * TAB character ({@code \t}) and each new line ({@code \n}) will be replaced with a pipe character ({@code |}).
052 * <p/>
053 * Each value is prepended to the argument Tuple in the order given above. Since the argument Tuple may vary
054 * in size, prepending the diagnostic value deterministically allows for simple grep and sed like commands to be
055 * applied to the files.
056 * <p/>
057 * Trap properties can be applied to a given Flow by calling {@link #buildProperties()} on the properties instance
058 * handed to the target {@link cascading.flow.FlowConnector} or directly to any given Tap via the
059 * {@link Tap#getConfigDef()} using {@link #setProperties(cascading.property.ConfigDef)}.
060 * <p/>
061 * It should be noted that traps are not intended for 'flow control' of a data stream. They are for exceptional
062 * cases that when reached should not cause a Flow to fail. Flow control (sending known bad data down a different
063 * branch) should be part of the application. Traps capture the values that are unaccounted for and cause errors,
064 * if missing them doesn't compromise the integrity of the application.
065 */
066public class TrapProps extends Props
067  {
068  public static final String RECORD_ELEMENT_TRACE = "cascading.trap.elementtrace.record";
069  public static final String RECORD_THROWABLE_MESSAGE = "cascading.trap.throwable.message.record";
070  public static final String RECORD_THROWABLE_STACK_TRACE = "cascading.trap.throwable.stacktrace.record";
071  public static final String LOG_THROWABLE_STACK_TRACE = "cascading.trap.throwable.stacktrace.log";
072  public static final String STACK_TRACE_LINE_TRIM = "cascading.trap.throwable.stacktrace.line.trim";
073  public static final String STACK_TRACE_LINE_DELIMITER = "cascading.trap.throwable.stacktrace.line.delimiter";
074
075  protected boolean recordElementTrace = false;
076  protected boolean recordThrowableMessage = false;
077  protected boolean recordThrowableStackTrace = false;
078
079  protected boolean logThrowableStackTrace = true;
080
081  protected boolean stackTraceTrimLine = true;
082  protected String stackTraceLineDelimiter = null;
083
084  public static TrapProps trapProps()
085    {
086    return new TrapProps();
087    }
088
089  public TrapProps()
090    {
091    }
092
093  /**
094   * Method recordAllDiagnostics enables recording of all configurable diagnostic values.
095   *
096   * @return this
097   */
098  public TrapProps recordAllDiagnostics()
099    {
100    recordElementTrace = true;
101    recordThrowableMessage = true;
102    recordThrowableStackTrace = true;
103
104    return this;
105    }
106
107  public boolean isRecordElementTrace()
108    {
109    return recordElementTrace;
110    }
111
112  /**
113   * Method setRecordElementTrace will enable recording the element trace value if set to {@code true}.
114   * <p/>
115   * The default is {@code false}.
116   *
117   * @param recordElementTrace of type boolean
118   * @return this
119   */
120  public TrapProps setRecordElementTrace( boolean recordElementTrace )
121    {
122    this.recordElementTrace = recordElementTrace;
123
124    return this;
125    }
126
127  public boolean isRecordThrowableMessage()
128    {
129    return recordThrowableMessage;
130    }
131
132  /**
133   * Method setRecordThrowableMessage will enable recording the Throwable message value if set to {@code true}.
134   * <p/>
135   * The default is {@code false}.
136   *
137   * @param recordThrowableMessage of type boolean
138   * @return this
139   */
140  public TrapProps setRecordThrowableMessage( boolean recordThrowableMessage )
141    {
142    this.recordThrowableMessage = recordThrowableMessage;
143
144    return this;
145    }
146
147  public boolean isRecordThrowableStackTrace()
148    {
149    return recordThrowableStackTrace;
150    }
151
152  /**
153   * Method setRecordThrowableStackTrace will enable recording the Throwable stacktrace value if set to {@code true}.
154   * <p/>
155   * The default is {@code false}.
156   *
157   * @param recordThrowableStackTrace of type boolean
158   * @return this
159   */
160  public TrapProps setRecordThrowableStackTrace( boolean recordThrowableStackTrace )
161    {
162    this.recordThrowableStackTrace = recordThrowableStackTrace;
163
164    return this;
165    }
166
167  public boolean isLogThrowableStackTrace()
168    {
169    return logThrowableStackTrace;
170    }
171
172  /**
173   * Method setLogThrowableStackTrace will disable logging of the Throwable stacktrace value if set to {@code false}.
174   * <p/>
175   * The default is {@code true}.
176   *
177   * @param logThrowableStackTrace of type boolean
178   * @return this
179   */
180  public TrapProps setLogThrowableStackTrace( boolean logThrowableStackTrace )
181    {
182    this.logThrowableStackTrace = logThrowableStackTrace;
183
184    return this;
185    }
186
187  public boolean isStackTraceTrimLine()
188    {
189    return stackTraceTrimLine;
190    }
191
192  /**
193   * Method setStackTraceTrimLine will disable trimming of whitespace on every recorded stacktrace line if set to
194   * {@code false}.
195   * <p/>
196   * The default is {@code true}.
197   *
198   * @param stackTraceTrimLine of type boolean
199   * @return this
200   */
201  public TrapProps setStackTraceTrimLine( boolean stackTraceTrimLine )
202    {
203    this.stackTraceTrimLine = stackTraceTrimLine;
204
205    return this;
206    }
207
208  public String getStackTraceLineDelimiter()
209    {
210    return stackTraceLineDelimiter;
211    }
212
213  /**
214   * Method setStackTraceLineDelimiter will set the text delimiter used to denote stacktrace lines.
215   * <p/>
216   * The default is {@code |} (the pipe character).
217   *
218   * @param stackTraceLineDelimiter of type boolean
219   * @return this
220   */
221  public TrapProps setStackTraceLineDelimiter( String stackTraceLineDelimiter )
222    {
223    this.stackTraceLineDelimiter = stackTraceLineDelimiter;
224
225    return this;
226    }
227
228  @Override
229  protected void addPropertiesTo( Properties properties )
230    {
231    properties.setProperty( RECORD_ELEMENT_TRACE, Boolean.toString( recordElementTrace ) );
232    properties.setProperty( RECORD_THROWABLE_MESSAGE, Boolean.toString( recordThrowableMessage ) );
233    properties.setProperty( RECORD_THROWABLE_STACK_TRACE, Boolean.toString( recordThrowableStackTrace ) );
234    properties.setProperty( LOG_THROWABLE_STACK_TRACE, Boolean.toString( logThrowableStackTrace ) );
235    properties.setProperty( STACK_TRACE_LINE_TRIM, Boolean.toString( stackTraceTrimLine ) );
236
237    if( stackTraceLineDelimiter != null )
238      properties.setProperty( STACK_TRACE_LINE_DELIMITER, stackTraceLineDelimiter );
239    }
240  }