code: private static void recursiveDeepToString(final Object o, final StringBuilder str, final Set<String> dejaVu) {
          if (appendSpecialTypes(o, str)) {
              return;
          }
          if (isMaybeRecursive(o)) {
              appendPotentiallyRecursiveValue(o, str, dejaVu);
          } else {
              tryObjectToString(o, str);
          }
      }
docs: This method performs a deep toString of the given Object.
  Primitive arrays are converted using their respective Arrays.toString methods while
  special handling is implemented for "container types", i.e. Object[], Map and Collection because those could
  contain themselves.
  <p>
  dejaVu is used in case of those container types to prevent an endless recursion.
  </p>
  <p>
  It should be noted that neither AbstractMap.toString() nor AbstractCollection.toString() implement such a
  behavior.
  They only check if the container is directly contained in itself, but not if a contained container contains the
  original one. Because of that, Arrays.toString(Object[]) isn't safe either.
  Confusing? Just read the last paragraph again and check the respective toString() implementation.
  </p>
  <p>
  This means, in effect, that logging would produce a usable output even if an ordinary System.out.println(o)
  would produce a relatively hard-to-debug StackOverflowError.
  </p>
  
  @param o      the Object to convert into a String
  @param str    the StringBuilder that o will be appended to
  @param dejaVu a list of container identities that were already used.