All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ml-modules.root.data-hub.4.impl.trace-lib.sjs Maven / Gradle / Ivy

There is a newer version: 6.1.1
Show newest version
/**
  Copyright (c) 2021 MarkLogic Corporation

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
*/
'use strict';

const config = require("/com.marklogic.hub/config.sjs");
const unSupportedLogMessage = `The 4.x version of the tracing library is no longer supported and has been deprecated in Datahub ${config.HUBVERSION}.`;


if (!this.rfc) {
  this.rfc = require("/data-hub/4/impl/run-flow-context.sjs");
}
const tracelib = require('/data-hub/4/impl/trace-lib.xqy');

let internalContexts = {
  currentTraceSettings: {}
}

function isObjectLike(value) {
  return value != null && typeof value == 'object';
}

function isString(value) {
  return typeof value == 'string' ||
    (!Array.isArray(value) && isObjectLike(value));
}

function isXmlNode(value) {
  return (value instanceof XMLNode && (value.nodeName !== null));
}

function isObject(value) {
  var type = typeof value;
  return value != null && (type == 'object' || type == 'function');
}

function setCurrentTraceSettings(settings) {
  internalContexts.currentTraceSettings = settings ? settings : {};
}

function getCurrentTraceSettings() {
  return internalContexts.currentTraceSettings;
}

function newTrace() {
  return {
    traceId: xdmp.random(),
    created: fn.currentDateTime()
  };
}

function enableTracing(enabled) {
  return tracelib.enableTracing(enabled);
}

function enabled() {
  return tracelib.enabled();
}

function hasErrors() {
  let ts = getCurrentTraceSettings();
  return !!ts['_has_errors'];
}

function incrementErrorCount() {
  let ts = getCurrentTraceSettings();
  ts.errorCount = getErrorCount() + 1;
}

function getErrorCount() {
  let ts = getCurrentTraceSettings();
  return ts.errorCount || 0;
}

function addFailedItem(item)
{
  getFailedItems().push(item);
}

function addCompletedItem(item) {
  getCompletedItems().push(item);
}

function getCurrentTrace(currentTrace) {
  if(!isTracingSupported()) {
    xdmp.log(unSupportedLogMessage, "warning");
    return {};
  }

  let ct = currentTrace || rfc.getTrace(rfc.getItemContext());
  if (!ct) {
    fn.error(xs.QName("MISSING_CURRENT_TRACE"));
  }
  return ct;
}

function setPluginLabel(label, currentTrace) {
  let ct = getCurrentTrace(currentTrace);
  ct.pluginLabel = label;
}

function getPluginLabel(currentTrace) {
  if(currentTrace) {
    return currentTrace.pluginLabel;
  }
  return null;
}

function resetPluginInput(currentTrace) {
  let ct = getCurrentTrace(currentTrace);
  ct.pluginInput = {};
}

function getPluginInput(currentTrace) {
  let o = currentTrace && currentTrace.pluginInput;
  if (o) {
    if (rfc.isJson()) {
      let oo = {};
      for (let key in o) {
        let value = o[key];
        oo[key] = sanitizeData(value);
      }
      return oo;
    }

    // for xml build this
    let inputs = [];
    for (let key in o) {
      const nb = new NodeBuilder();
      nb.startElement(key);
      let value = sanitizeData(o[key]);
      if (value) {
        if (isXmlNode(value)) {
          nb.addNode(value);
        }
        else {
          nb.addText(value.toString());
        }
      }
      nb.endElement();
      inputs.push(nb.toNode());
    }
    return Sequence.from(inputs);
  }
  return null;
}

function setPluginInput(label, input, currentTrace) {
  let ct = getCurrentTrace(currentTrace);
  let existing = ct.pluginInput || {};
  existing[label] = input;
  ct.pluginInput = existing;
}

function getCompletedItems() {
  let ts = getCurrentTraceSettings();
  if (ts.completedItems) {
    return ts.completedItems;
  }

  let value = [];
  ts.completedItems = value;
  return value;
}

function getFailedItems() {
  let ts = getCurrentTraceSettings();
  if (ts.failedItems) {
    return ts.failedItems;
  }

  let value = [];
  ts.failedItems = value;
  return value;
}

function writeTrace(itemContext) {
  if(!isTracingSupported()) {
    xdmp.log(unSupportedLogMessage.concat("Error Trace is not written"), "warning");
    return;
  }
  let identifier = rfc.getId(itemContext);
  if (identifier) {
    addCompletedItem(identifier);
  }
  writeErrorTrace(itemContext);
}

function writeErrorTrace(itemContext) {
  if(!isTracingSupported()) {
    xdmp.log(unSupportedLogMessage.concat("Error Trace is not written"), "warning");
    return;
  }
  if (enabled() || hasErrors()) {
    let currentTrace = rfc.getTrace(itemContext);
    if(!currentTrace) {
      return;
    }
    let trace = null;
    if (rfc.isJson()) {
      trace = {
        trace: {
          jobId: rfc.getJobId(),
          format: rfc.getDataFormat(),
          traceId: currentTrace.traceId,
          created: currentTrace.created,
          identifier: rfc.getId(itemContext),
          flowName: rfc.getFlowName(),
          flowType: rfc.getFlowType(),
          hasError: false,
          steps: []
        }
      };
      let i;
      if (currentTrace && currentTrace.traceSteps) {
        for (i = 0; i < currentTrace.traceSteps.length; i++) {
          let step = currentTrace.traceSteps[i];
          trace.trace.steps.push(step);
          if (step.error) {
            trace.trace.hasError = true;
          }
        }
      }
    }
    else {
      const nb = new NodeBuilder();
      nb.startDocument();
        nb.startElement("trace");
          nb.startElement("jobId");
            nb.addText(rfc.getJobId() ? rfc.getJobId().toString() : "");
          nb.endElement();
          nb.startElement("format");
            nb.addText(rfc.getDataFormat() ? rfc.getDataFormat().toString() : "");
          nb.endElement();
          nb.startElement("traceId");
            nb.addText(currentTrace.traceId ? currentTrace.traceId.toString() : "");
          nb.endElement();
          nb.startElement("created");
            nb.addText(currentTrace.created ? currentTrace.created.toString() : "");
          nb.endElement();
          nb.startElement("identifier");
            nb.addText(rfc.getId(itemContext) ? rfc.getId(itemContext).toString() : "");
          nb.endElement();
          nb.startElement("flowName");
            nb.addText(rfc.getFlowName() ? rfc.getFlowName().toString() : "");
          nb.endElement();
          nb.startElement("flowType");
            nb.addText(rfc.getFlowType() ? rfc.getFlowType().toString() : "");
          nb.endElement();
          let hasErrors = false;
          nb.startElement("steps");
            let i;
            if (currentTrace && currentTrace.traceSteps) {
              for (i = 0; i < currentTrace.traceSteps.length; i++) {
                let step = currentTrace.traceSteps[i];
                nb.startElement("step")
                nb.startElement("label");
                nb.addText(step.label.toString());
                nb.endElement();
                nb.startElement("input");
                if (step.input) {
                  if (step.input instanceof Sequence) {
                    for (let i of step.input ) {
                      nb.addNode(i);
                    }
                  } else if (isXmlNode(step.input)) {
                    nb.addNode(step.input);
                  } else {
                    nb.addText(JSON.stringify(step.input));
                  }
                }
                nb.endElement();
                nb.startElement("output");
                if (step.output) {
                  if (isXmlNode(step.output)) {
                    nb.addNode(step.output);
                  } else if (isString(step.output)) {
                    nb.addText(step.output.toString());
                  } else {
                    nb.addText(step.output.toString());
                  }
                }
                nb.endElement();
                nb.startElement("error");
                if (step.error) {
                  hasErrors = true;
                  if (isXmlNode(step.error)) {
                    nb.addNode(step.error);
                  } else {
                    nb.addText(JSON.stringify(step.error));
                  }
                }
                nb.endElement();
                nb.startElement("duration");
                if (step.duration) {
                  nb.addText(step.duration.toString());
                }
                nb.endElement();
                nb.startElement("options");
                if (step.options) {
                  if (isXmlNode(step.options)) {
                    nb.addNode(step.options);
                  } else {
                    nb.addText(JSON.stringify(step.options));
                  }
                }
                nb.endElement();
                nb.endElement();
              }
            }
          nb.endElement();
          nb.startElement("hasError");
          nb.addText(hasErrors.toString());
          nb.endElement();
        nb.endElement();
      nb.endDocument();
      trace = nb.toNode();
    }
    let extension = rfc.isJson() ? '.json' : '.xml';
    xdmp.eval(
      'xdmp.documentInsert("/' + currentTrace.traceId + extension + '",' +
      'trace,' +
      'xdmp.defaultPermissions(),' +
      '["trace", "' + rfc.getFlowType() + '"])',
    {
      trace: trace
    },
    {
      database: xdmp.database(config.TRACEDATABASE),
      commit: 'auto',
      update: 'true',
      ignoreAmps: true
    })
  }
}

function sanitizeData(data) {
  if (!data) {
    return null;
  }

  let result = data;
  if (data instanceof BinaryNode) {
    result = xs.hexBinary(data);
  }
  else if (!rfc.isJson() && !isXmlNode(data)) {
    result = xdmp.quote(data);
  }
  return result;
}

function pluginTrace(itemContext, output, duration) {
  if(!isTracingSupported()) {
    xdmp.log(unSupportedLogMessage.concat("Plugin Trace is not logged"), "warning");
    return;
  }
  let ic = itemContext || rfc.getItemContext();
  let currentTrace = rfc.getTrace(ic);
  output = sanitizeData(output);

  if (enabled()) {
    let input = getPluginInput(currentTrace);
    let options = rfc.getOptions(ic);
    let newStep  = {
      label: getPluginLabel(currentTrace),
      input: input,
      output: output,
      duration: duration,
      options: options
    };
    let steps = currentTrace.traceSteps || [];
    steps.push(newStep);
    currentTrace.traceSteps = steps;
  }
}

function errorTrace(itemContext, error, duration) {
  if(!isTracingSupported()) {
    xdmp.log(unSupportedLogMessage.concat("Error Trace is not logged"), "warning");
    return;
  }
  let currentTrace = rfc.getTrace(itemContext);
  let identifier = rfc.getId(itemContext);
  incrementErrorCount();
  if (identifier) {
    addFailedItem(identifier);
  }
  let ts = getCurrentTraceSettings();
  ts['_has_errors'] = true;
  let traceSteps = currentTrace.traceSteps || [];

  traceSteps.push({
    label: getPluginLabel(currentTrace),
    input: getPluginInput(currentTrace),
    error: rfc.isJson() ? error : error,
    duration: duration,
    options: rfc.getOptions(itemContext)
  });
  currentTrace.traceSteps = traceSteps;
  writeErrorTrace(itemContext);
  ts['_has_errors'] = false; //resetting the flag after writing error trace
}


function findTraces(q, page, pageLength) {
  if(!isTracingSupported()) {
    xdmp.log(unSupportedLogMessage.concat("No traces found"), "warning");
    return {};
  }
  return tracelib.findTraces(q, page, pageLength);
}

function getTraces(page, pageLength) {
  if(!isTracingSupported()) {
    xdmp.log(unSupportedLogMessage.concat("No traces found"), "warning");
    return {};
  }
  return tracelib.getTraces(page, pageLength);
}

function getTrace(id) {
  if(!isTracingSupported()) {
    xdmp.log(unSupportedLogMessage.concat("No trace found"), "warning");
    return {};
  }
  return tracelib.getTrace(id);
}

function isTracingSupported() {
  return tracelib.isTracingSupported();
}

module.exports = {
  setCurrentTraceSettings: setCurrentTraceSettings,
  getCurrentTraceSettings: getCurrentTraceSettings,
  newTrace: newTrace,
  enableTracing: enableTracing,
  enabled: enabled,
  hasErrors: hasErrors,
  incrementErrorCount: incrementErrorCount,
  getErrorCount: getErrorCount,
  addFailedItem: addFailedItem,
  getFailedItems: getFailedItems,
  addCompletedItem: addCompletedItem,
  getCompletedItems: getCompletedItems,
  setPluginLabel: setPluginLabel,
  getPluginLabel: getPluginLabel,
  resetPluginInput: resetPluginInput,
  getPluginInput: getPluginInput,
  setPluginInput: setPluginInput,
  writeTrace: writeTrace,
  writeErrorTrace: writeErrorTrace,
  pluginTrace: pluginTrace,
  errorTrace: errorTrace,
  findTraces: findTraces,
  getTraces: getTraces,
  getTrace: getTrace,
  isXmlNode: isXmlNode,
  isTracingSupported: isTracingSupported,
  unSupportedLogMessage: unSupportedLogMessage
};




© 2015 - 2024 Weber Informatics LLC | Privacy Policy