• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

FIWARE / contract-management / #102

30 Sep 2026 10:28AM UTC coverage: 4.485% (+0.7%) from 3.796%
#102

Pull #28

vramperez
Remove stack trace floods from connection errors

- Micronaut's DefaultHttpClient logged every failed connection at ERROR with a
  ~125 line stack trace, duplicating the one-line WARN of the failed call. It is
  off by default now (LOG_LEVEL_MICRONAUT_HTTP_CLIENT to bring it back)
- The subscription retry warning names the event and the reason instead of
  attaching the stack trace on every retry
- Suppress logback's status dump on startup

Without a reachable TM Forum API this reduces 25s of logs from ~1100 to ~60 lines.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Pull Request #28: Add failure reasons, downstream error descriptions and configurable log output

229 of 453 new or added lines in 31 files covered. (50.55%)

11 existing lines in 7 files now uncovered.

1608 of 35855 relevant lines covered (4.48%)

0.04 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.66
/src/main/java/org/fiware/iam/logging/DownstreamError.java
1
package org.fiware.iam.logging;
2

3
import io.micronaut.http.client.exceptions.HttpClientResponseException;
4

5
import java.util.ArrayList;
6
import java.util.List;
7
import java.util.Optional;
8

9
/**
10
 * Renders a failure as one line that tells what went wrong - including the status and body a downstream service
11
 * answered with - so it can be logged without a stack trace.
12
 */
13
public final class DownstreamError {
14

15
        static final int MAX_BODY_LENGTH = 1000;
16
        private static final int MAX_CAUSE_DEPTH = 5;
17

18
        private DownstreamError() {
19
        }
20

21
        /**
22
         * Describe the given throwable and its causes, e.g.
23
         * {@code [pap_rejected_policy] PAP rejected policy x <- status=400 body={"detail":"..."}}
24
         */
25
        public static String describe(Throwable throwable) {
26
                List<String> parts = new ArrayList<>();
1 ✔
27
                Throwable current = throwable;
1 ✔
28
                while (current != null && parts.size() < MAX_CAUSE_DEPTH) {
1 ✔
29
                        String part = describeSingle(current);
1 ✔
30
                        if (parts.isEmpty() || !parts.getLast().contains(part)) {
1 ✔
31
                                parts.add(part);
1 ✔
32
                        }
33
                        if (current.getCause() == current) {
1 ✔
NEW
34
                                break;
×
35
                        }
36
                        current = current.getCause();
1 ✔
37
                }
1 ✔
38
                return String.join(" <- ", parts);
1 ✔
39
        }
40

41
        /**
42
         * Status and body of an http error, empty for all other throwables.
43
         */
44
        public static Optional<String> describeHttpError(Throwable throwable) {
45
                if (throwable instanceof HttpClientResponseException hcre) {
1 ✔
46
                        return Optional.of("status=%s body=%s".formatted(hcre.getStatus().getCode(), body(hcre)));
1 ✔
47
                }
48
                return Optional.empty();
1 ✔
49
        }
50

51
        private static String describeSingle(Throwable throwable) {
52
                return describeHttpError(throwable)
1 ✔
53
                                .orElseGet(() -> Optional.ofNullable(throwable.getMessage())
1 ✔
54
                                                .map(message -> throwable instanceof RuntimeException && isOwnException(throwable)
1 ✔
55
                                                                ? message
1 ✔
56
                                                                : "%s: %s".formatted(throwable.getClass().getSimpleName(), message))
1 ✔
57
                                                .orElse(throwable.getClass().getSimpleName()));
1 ✔
58
        }
59

60
        private static boolean isOwnException(Throwable throwable) {
61
                return throwable.getClass().getPackageName().startsWith("org.fiware.iam");
1 ✔
62
        }
63

64
        private static String body(HttpClientResponseException hcre) {
65
                String body;
66
                try {
67
                        body = hcre.getResponse().getBody(String.class).orElse("");
1 ✔
NEW
68
                } catch (RuntimeException e) {
×
NEW
69
                        body = "";
×
70
                }
1 ✔
71
                if (body.isBlank()) {
1 ✔
72
                        return "<empty>";
1 ✔
73
                }
74
                body = body.replaceAll("\\s*\\R\\s*", " ");
1 ✔
75
                return body.length() > MAX_BODY_LENGTH ? body.substring(0, MAX_BODY_LENGTH) + "...(truncated)" : body;
1 ✔
76
        }
77
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc