(NC-194) Log exceptions caught by middleware

Log exceptions caught in middleware at the WARN level. This adds a small
helper function that also logs the request method and URI. All the middleware
wrappers are moved into a single composed wrapper in middleware, so that it's
possible to independently test the full middleware stack and know it's the
same used on the real app.
Merge pull request #63 from pcarlisle/nc-194-log-stack-traces

(NC-194) Log exceptions caught by middleware
(NC-198) Add classification/inheritance-maxima.

This function, when given a map of groups to their ancestors, returns
those groups that are maxima according to the partial ordering defined
by inheritance (where a < b iff a is an ancestor of b). The returned
groups are not less than any other given group by this ordering, meaning
that they are unrelated to all other returned groups.
(NC-198) Add classification/conflicts.

Add a new schema, ClassificationConflict, for the description of
conflicts between classifications, which looks like a Classification
except that the values of all maps are sets of Classification values.

Add classification/conflicts, which takes classifications and returns
a ClassificationConflict map with the conflicts, or nil if there are no
conflicts. Also add some tests for this function.
(NC-198) Middleware to explain classification conflicts.

Add a new middleware, `wrap-classification-conflict-explanations`, that
catches a classification-conflict slingshot exception (to be thrown by
the classification handler if it encounters a conflict) and creates
a 500 from the conflicts, matching groups, and their ancestors that
explains to the user, for each conflicting value, what it is, what
matched group caused it to be added to the classification, and what
group actually defined that value (which may be an ancestor of the
matched group).
(maint) Remove redundant :classes from Group schema.
(NC-198) All ClassificationConflict schema keys are optional.
(NC-198) Improve classification behavior.

The classification route checks the classifications from all groups the
node matches to see if they are disjoint. If they are, they are merged
into a single classification that is returned to the client. If not, an
error is thrown that is caught by the classification conflict
explanation middleware and transformed into a 500 response with details
about the conflicting values and where they came from.
(NC-198) Improve classification conflict error msg.

In the human-readable message, name the groups that the node was
classified into, and explain what conflicted (the environment field,
names of classes with conflicting parameters, names of variables with
conflicting values).