code: @SuppressWarnings("unchecked")
      @Trivial
      private JSONObject toJSONObjectForThrowable(Map<String, ?> errorInfo, Throwable error) {
          if (TraceComponent.isAnyTracingEnabled() && tc.isDebugEnabled())
              Tr.debug(this, tc, "toJSONObjectForThrowable", errorInfo, error);
  
          LinkedHashMap<String, List<?>> addedInfo = new LinkedHashMap<String, List<?>>();
          for (Map.Entry<String, ?> entry : errorInfo.entrySet()) {
              Object value = entry.getValue();
              if (value instanceof List)
                  addedInfo.put(entry.getKey(), (List<?>) value);
          }
  
          JSONObject json = new OrderedJSONObject();
  
          int index = 0;
          JSONObject current = json;
          Set<Throwable> causes = new HashSet<Throwable>(); // avoid cycles in exception chain
          for (Throwable cause = error; cause != null && causes.add(cause); index++) {
              // custom attributes for position in exception chain (for example, sqlState and errorCode for dataSource)
              for (Map.Entry<String, List<?>> entry : addedInfo.entrySet()) {
                  List<?> values = entry.getValue();
                  if (values.size() > index) {
                      Object value = values.get(index);
                      if (value != null)
                          current.put(entry.getKey(), value);
                  }
              }
  
              current.put("class", cause.getClass().getName());
              current.put("message", cause.getMessage());
  
              JSONArray stack = new JSONArray();
              for (StackTraceElement element : cause.getStackTrace())
                  stack.add(element.toString());
              current.put("stack", stack);
  
              if ((cause = cause.getCause()) != null) {
                  Map<String, Object> parent = current;
                  parent.put("cause", current = new OrderedJSONObject());
              }
          }
  
          return json;
      }
docs: Populate JSON object for a top level exception or error.
  
  @param errorInfo additional information to append to exceptions and causes
  @param error     the top level exception or error.
  @return JSON object representing the Throwable.