/**
   * Log the task failure
   *
   * @param taskId the task that failed
   * @param taskType the type of the task
   * @param time the time of the failure
   * @param error the error the task failed with
   * @param failedDueToAttempt The attempt that caused the failure, if any
   */
  public void logTaskFailed(
      TaskID taskId, String taskType, long time, String error, TaskAttemptID failedDueToAttempt) {
    if (disableHistory) {
      return;
    }

    JobID id = taskId.getJobID();
    if (!this.jobId.equals(id)) {
      throw new RuntimeException("JobId from task: " + id + " does not match expected: " + jobId);
    }

    if (null != writers) {
      String failedAttempt = failedDueToAttempt == null ? "" : failedDueToAttempt.toString();
      log(
          writers,
          RecordTypes.Task,
          new Keys[] {
            Keys.TASKID, Keys.TASK_TYPE,
            Keys.TASK_STATUS, Keys.FINISH_TIME,
            Keys.ERROR, Keys.TASK_ATTEMPT_ID
          },
          new String[] {
            taskId.toString(),
            taskType,
            Values.FAILED.name(),
            String.valueOf(time),
            error,
            failedAttempt
          });
    }
  }
  /**
   * Log failed reduce task attempt.
   *
   * @param taskAttemptId task attempt id
   * @param timestamp time stamp when task failed
   * @param hostName host name of the task attempt.
   * @param error error message of the task.
   * @param taskType Whether the attempt is cleanup or setup or reduce
   */
  public void logReduceTaskFailed(
      TaskAttemptID taskAttemptId, long timestamp, String hostName, String error, String taskType) {
    if (disableHistory) {
      return;
    }

    JobID id = taskAttemptId.getJobID();
    if (!this.jobId.equals(id)) {
      throw new RuntimeException("JobId from task: " + id + " does not match expected: " + jobId);
    }

    if (null != writers) {
      log(
          writers,
          RecordTypes.ReduceAttempt,
          new Keys[] {
            Keys.TASK_TYPE, Keys.TASKID,
            Keys.TASK_ATTEMPT_ID, Keys.TASK_STATUS,
            Keys.FINISH_TIME, Keys.HOSTNAME,
            Keys.ERROR
          },
          new String[] {
            taskType,
            taskAttemptId.getTaskID().toString(),
            taskAttemptId.toString(),
            Values.FAILED.name(),
            String.valueOf(timestamp),
            hostName,
            error
          });
    }
  }
  /**
   * Logs job failed event. Closes the job history log file.
   *
   * @param timestamp time when job failure was detected in ms.
   * @param finishedMaps no finished map tasks.
   * @param finishedReduces no of finished reduce tasks.
   */
  public void logFailed(long timestamp, int finishedMaps, int finishedReduces, Counters counters) {
    if (disableHistory) {
      return;
    }

    if (null != writers) {
      log(
          writers,
          RecordTypes.Job,
          new Keys[] {
            Keys.JOBID, Keys.FINISH_TIME,
            Keys.JOB_STATUS, Keys.FINISHED_MAPS,
            Keys.FINISHED_REDUCES, Keys.COUNTERS
          },
          new String[] {
            jobId.toString(),
            String.valueOf(timestamp),
            Values.FAILED.name(),
            String.valueOf(finishedMaps),
            String.valueOf(finishedReduces),
            counters.makeEscapedCompactString()
          },
          true);
      closeAndClear(writers);
    }
  }