001    /*
002     * Copyright (c) 2007-2015 Concurrent, 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    
021    package cascading.tap;
022    
023    import java.util.Properties;
024    
025    import 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     */
066    public 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      }