    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  }