Thursday, June 20, 2019

Workflow Monitoring - On Demand

The out of the box Workflow Monitoring level specified when deploying a version of a workflow is a great tool but it requires it having been explicitly turned on in order to be of value. I have found that while this was turned on for all users in a production environment, we would have a massive surplus of log data that would fill up the logging tables unnecessarily, some users logins may be setup to only do troubleshooting. Alternatively, a specific user could have logging turned up for so that all workflows are tracked rather than having to know all the workflow process names where an error is going to occur as the user may not know before hand which workflow is causing the problem. What is needed is to add logic to the 'Workflow Process Manager' business service.  The PreInvoke needs the following script added:

function Service_PreInvokeMethod (MethodName, Inputs, Outputs) {
  if (MethodName.indexOf("Run") >=0) {
    TheApplication().Utility.logRaiseWF(Inputs, MethodName);
  }
  return (ContinueOperation);
}

The Invoke needs the following script added:

function Service_InvokeMethod (MethodName, Inputs, Outputs) {
  if (MethodName.indexOf("Run") >=0) {
    TheApplication().Utility.logResetWF(Inputs, MethodName);
  }
}

The Utility service is enabled in the Application start event. The 'XXX Utilities' business service uses two additional methods.

function logRaiseWF(Inputs, callingMethod) {
// Method called from Workflow Process Manager, PreInvoke method for methods having 'Run' in the method name if the
// Utilities Service is enabled for the OM Component the session is running within.

  //If The Troubleshoot process property is passed (when invoking from a Named Method user prop) or as a child prop (when
  //invoked from a WF as a subprocess) or the user's log level is 3 or higher, temporarily set WF monitoring for the WF
  //process to detail.  logResetWF is called immediately after invokation to turn WF monitoring off.
  if (gCurrentLogLvl >= 3 || 
   Inputs.GetProperty("Troubleshoot") == "Y" || 
   (Inputs.GetChildCount() > 0 && Inputs.GetChild(0).GetProperty("Troubleshoot") == "Y")) { 
    if (TheApplication().GetProfileAttr("IsStandaloneWebClient") == "TRUE") {
      logStep("PPT Utilities.logRaiseWF.callingMethod: "+callingMethod+" - ProcessName: "+Inputs.GetProperty("ProcessName")); 
      logPS(Inputs);
    } else {
      logSetWFLevel(Inputs, "3 - Detail");
    }
  }

  //The first WF Called in a stack uses the RunProcess method.  If subprocess is called from a WF, the _ method is used. 
  //All payloads captured after the initial RunProcess call are stored in an array and dumped if an error occurs.  If 
  //system preference 'PPT Hold Buffer Max' is greater than 0 then the array is instead always kept at that count rather
  //than resetting on WF start
  if (gsTraceIntfaceReqResp != "FALSE" && callingMethod.indexOf("Run")== 0 && gHoldBufferMax == 0) gHoldReqResp = [];
}

function logResetWF(Inputs, callingMethod) {
// Method called from Workflow Process Manager, Invoke method for methods having 'Run' in the method name if the
// Utilities Service is enabled for the OM Component the session is running within.

  //Conditions controlling whether to reset WF monitoring level must mirror those in the logRaiseWF function
  if (gCurrentLogLvl >= 3 || 
   Inputs.GetProperty("Troubleshoot") == "Y" || 
   (Inputs.GetChildCount() > 0 && Inputs.GetChild(0).GetProperty("Troubleshoot") == "Y")) { 
    if (TheApplication().GetProfileAttr("IsStandaloneWebClient") == "TRUE") {
      logStep("PPT Utilities.logResetWF.callingMethod: "+callingMethod+" - ProcessName: "+Inputs.GetProperty("ProcessName")); 
    } else {
      logSetWFLevel(Inputs, "0 - None");
    }
  }
}

Finally, these wrapper functions call a function to actually set and reset the monitoring level on a deployed workflow:

function logSetWFLevel (Inputs, logLevel) {
//Use:  
//Returns: 
  var boWF:BusObject = TheApplication().GetBusObject("PPT Workflow Process Deployment");
  var bcWF:BusComp = boWF.GetBusComp("Workflow Process Deployment");
  var found:Boolean = false;
  var propName = Inputs.GetFirstProperty();

  while (propName != "") {
    if (propName.indexOf("ProcessName") == 0) {
      with (bcWF) {
        ActivateField("Monitoring Level");
        ClearToQuery();
        SetSearchSpec("Name", Inputs.GetProperty(propName));
        SetSearchSpec("Deployment Status", "Active");
        ExecuteQuery(ForwardOnly);
        found = FirstRecord();
        if (found) {
          SetFieldValue("Monitoring Level", logLevel);
          WriteRecord();
        }
      }     
    }
    propName = Inputs.GetNextProperty();
  }
}

To minimize the times where logging is actually raised, one of the following conditions must be true:

  • An input process property called 'Troubleshoot' has a 'Y' value.  I use this process prop across most of my workflows and only pass the 'Y' value when passed as a command/Named Method BC User property from a button on an admin view or an applet gear menu option.  It is useful to expose manually triggering a workflow this way to replicate a process that is normally triggered by the system in some way.
  • Users log level is 3 or higher

Note that if using the thick client, logging is NOT turned on so minimize SQL in the siebel.log file and since this information can easily be set through the SIEBEL_LOG_EVENTS system variable along with the Step and Process execution events.

No comments:

Post a Comment