Getting Stack Trace in Boundary Error Event

When using the boundary error event, any stack trace output does not appear in the logs (in our case the WildFly server.log file). As have been possibly noted in past posts, it is possible in 7.6+ to set a variable that will capture the error message itself. However, the stack trace is no where to be found.

When handling an exception in this manner, no incident is generate and thus there’s no possibility of find that stack trace in the variable history. Moreover, we are setting history level to audit and then activity, which means that we wouldn’t probably even have those options.

Do we have to capture the stack trace ourselves in the class/script where it occurred and somehow pass that out of that class/script to a history log or other such repository?

Thanks.

Michael

Hi Michael,

My guiding principles are;
Use BPMN error for business errors
Use incidents for technical errors.

Hence under this circumstance, there is no stack trace for a business error. Business errors are hings like missing data or out of bounds etc.

If you need to take action when an incident occurs, I use a custom incident handler.

regards

Rob

What do you mean “use incidents”. This is our situation.

  • We need to turn history-level down to activity. When this is done, no historical incidents are recorded.
  • We need to gracefully handle errors, which I believe means using the a boundary error event.
  • If a boundary error event is used, the stack trace that results from an exception is not available to Camunda and is not output to any log by Camunda itself.

Therefore, and this is what I was asking, to capture the stack trace for debugging or historical analysis, the class or script itself must implement a try/catch block, which will allow the stack trace to be sent to a log, and/or assigned to a process variable, and/or handled by a custom history function (which is what we’re implementing). If there is a way to extract that stack trace under these circumstances, it would be great to know. Otherwise, I just wanted to confirm my understanding of what we must do.

Thanks.

Hi,

I guess what Im trying to say is theres a difference between throw new BpmnError(…) versus throw new Exception(…).

A BpmnError is ‘caught’ at the process model level via the catch error event - hence I use this to capture business exceptions where a business exception may for example be customer credit exhausted. Thus there is no stack trace for a business exception, its a violation of a business rule, in this case a customer must have sufficient credit to purchase for example.

I tend not to model technical exceptions explicitly in the process model as they should not be a business concern. Thus if I need to take some remedial action, eg raise a service request, I use an incident handler to raise the service request. Thus incident handlers take over the ‘flow’. In this circumstance, a technical error results in an incident. When an incident occurs, the process instance will be suspended, a stacktrace recorded and my custom incident handlers can initiate remedial action. When the technical error is cleared, resume the process logic.

It was tempting for our developers to use BpmnErrors for catching technical exceptions, but we found it was better to keep business and technical exception handling under two distinct paradigms rather than re-using the BpmnError construct for both…

Custom incident handler section can be found in [1]

regards

Rob

[1[] https://docs.camunda.org/manual/7.6/user-guide/process-engine/incidents/#implement-custom-incident-handlers

My goal is to handle anything that might throw a stack trace in defined manner. If you use the boundary error event, that stack trace is not recorded by Camunda anywhere, nor does Camunda make it available as a process variable the way it makes the error message available via the errorMessageVariable.

If the exception is thrown in an external class or script, you can capture it in try/catch block. If the Camunda engine were to throw it during execution of the task, it would be lost as it will not show up in the log nor would it apparently be written to the database because you “trapped” the error, which I assume tells Camunda, “I got this, you don’t need to do anything.”

What you’re suggesting is that I will need to implement a custom incident handler if I want to do the following:

  • Capture any and all stack traces, no matter what their origin
  • Use boundary error events so that I can handle errors in a predefined manner
  • Turn history down to the activity level

Is that correct?

Michael

Based on the “Incidents” ideas, I dug a little into the Java api to see what we can do without to much modification…

Came up with the following:

Where Generate Incident is:

var IncidentEntity  = Java.type('org.camunda.bpm.engine.impl.persistence.entity.IncidentEntity');

var IncidentContext = Java.type('org.camunda.bpm.engine.impl.incident.IncidentContext');

var context = new IncidentContext();
context.setActivityId(execution.getCurrentActivityId());
context.setExecutionId(execution.getProcessInstanceId());
context.setProcessDefinitionId(execution.getProcessDefinitionId());

var newIncident  = IncidentEntity.createAndInsertIncident("myCustomIncidentType", context, "A custom incident message.");

newIncident.id

I store the newIncident.id as a process variable so in the next task i can search for it and resolve it.

The Resolve Incident task has a Execution Listener with a End type:

execution.getProcessEngineServices().getRuntimeService().createIncidentQuery().incidentId(execution.getVariable('IncidentId')).singleResult().resolve();

This finds the Incident with the IndicidentId process variable as the filter and resolves it. I had to do this, because as far as i can tell there is no Web API to resolve incidents? Incident | docs.camunda.org But maybe i am missing something?

Where .createIncidentQuery() returns a instance of IncidentEntity, and .resolve() is (IncidentEntity (camunda BPM Javadocs 7.6.13-ee))

In Cockpit there is not ability to “resolve” the incidents as a action. Would be nice to have a “generic” resolve function in Cockpit, so you can generate a Incident and resolve it without having to write and deploy custom Java.

Some screenshots of what it looks like in Cockpit:

With Two process instances (1 with a incident, and the other had its incident resolved)

Instance with the Incident:

Instance with the Incident, in the Incidents Tab:

Instance with its Incident Resolved:


References:

  1. General Info: Incidents | docs.camunda.org
  2. Example I based my search on: camunda-7-code-examples/CreateCustomIncidentTaskListener.java at master · camunda-consulting/camunda-7-code-examples · GitHub
  3. Important!: The Incident Entity I create: IncidentEntity (camunda BPM Javadocs 7.6.13-ee)
  4. How i find the Entity to resolve it in another task: IncidentQuery (camunda BPM Javadocs 7.6.13-ee)
  5. Important!: How to create the Context object which is used in #3 above: IncidentContext (camunda BPM Javadocs 7.6.13-ee)

@camunda would be great to hear your thoughts on the above usage of the internal api. Thanks!

I will respond after I fully digest this. You’re using things I’ve never used and there are lots of “holes” in my knowledge.

My initial thinking at this point would be to implement a universal incident handler that did nothing more than grab the error message, stack trace, and identifying UUIDs and dump them into an external database.

I’ve noticed that if you don’t terminate the process, then the incident stays in act_ru_incidents as long as the process is running, even if you have history-level set to activity. So, I thought maybe the boundary error event could fire code to pull it from that table, though that seems a bit more complex.

Thanks, Stephen, for the detailed post, it’s impressive!

I’ll get back to you.

Michael

Stephen,

I’ve started to play around with this and now must reveal some ignorance (feel free to point and laugh). I realize that a full explanation may exceed the scope of this forum (i.e. “Why don’t you go out and learn Java, Web Services, Spring, etc., etc., first, then come back and ask questions?”, which in some contexts is fair, but don’t get me started on Camunda’s “ease of use”).

  • I assume the code in the first panel above is Groovy (at least Eclipse doesn’t complain about it).
  • I’ve looked at the references above. In particular the example code in Github (which won’t compile as there’s an error in ig). That might be a good option.
  • I’ve no idea how to implement this on a “global” basis.
  • I’ve read through the references provided and quite frankly don’t have the level of experience to fully understand all of them. For example, you indicate that the “context” is important, but I’m not entirely certain what that means in this context (yes, pun intended).

The goal here is to globally replace the current incident handler with one that will take the items in the list below and put them into a database. I would want this to be automatic so that developers don’t need to include any special code in their processes, scripts, or classes. In effect, I want it to work much like it does with the default incident handler when full history is turned on, but the data is written elsewhere. I can handle the actual writing of the data to the database.

As for incident resolution, that is a different mechanism and not relevant to debugging, though I appreciate your describing its use here.

  • Process Instance ID
  • Process Key (name of the process)
  • Activity Instance ID
  • Activity ID (name)
  • Event ID (start or end)
  • Error Message (accessible through the errorMessageVariable extension)
  • Error Code (accessible through the errorCodeVariable extension)
  • Stack Trace

The code above is Javascript

The solution i provided was a example of using Javascript to execute the creation and resolve of Camunda Incidents.

In practice you would wrap my code in a function, and then use the load('http://myServer/blah/blah/myscript.js') to load the script and call the function when you want to “throw a incident”. If you are looking to do this on a global level, then you should look at deploying custom java incident types/handlers as described in the Camunda docs.

My solution was/is more of a javascript/script based variation that lets you create and resolve incidents without deploying new Java code.

Based on the JavaDocs, the Incidents work as follow:

  1. The IncidentEntity is the actual “Incident”
  2. The IncidentContext is what links the IncidentEntity to a specific Process Instance, Job, and/or Activity. If you dont provide the right context, then the incident is created, but you would be able to know what process instance/job/activity is belongs to.

Also just found this:

https://app.camunda.com/jira/browse/CAM-1689 (I can create / resolve an incident through public API)

So clearly a open issue / feature request for a while.

EDIT: I moved the Incidents example into its own thread for better discoverability: Create and Resolve Incidents through Scripts(Javascript examples) (Internal API)

Linking to the specific incident isn’t that important unless we’re trying to chain in some sort of activity where I would be concerned about a persistent incident object somewhere, I just want the stack trace.

It sounds like this is going to be difficult to do. It’s unfortunate that there’s not the equivalent to an activity listener for incidents, but it is what it is. I absolutely do not want to modify Camunda itself. Whatever I do must be configurable in the Java container (e.g. WildFly) in the same manner that custom history handlers are. That may not be possible.