001package io.jstach.rainbowgum;
002
003import java.lang.System.Logger.Level;
004import java.util.List;
005import java.util.Objects;
006import java.util.concurrent.ExecutionException;
007
008/**
009 * A container for the response of some sort of request or action. Logging is usually with
010 * out a return value but there are certain scenarios where a return value is needed
011 * particularly when doing health checks.
012 *
013 * @apiNote At the moment this class is largely an internal detail as extension points do
014 * not create responses however {@link #status()} objects are created by extension points.
015 * @see Status
016 */
017public sealed interface LogResponse {
018
019        /**
020         * Configuration name of the component.
021         * @return name.
022         */
023        public String name();
024
025        /**
026         * Status of the response.
027         * @return status.
028         */
029        public Status status();
030
031        /**
032         * Component type.
033         * @return interface class.
034         */
035        public Class<?> type();
036
037        /**
038         * Log component status check. Note that trees or aggregates can be created with
039         * {@link AggregateStatus}.
040         */
041        sealed interface Status {
042
043                /**
044                 * Level is used here to indicate the severity of the status.
045                 * @return level.
046                 */
047                public System.Logger.Level level();
048
049                /**
050                 * Standard status.
051                 */
052                public enum StandardStatus implements Status {
053
054                        /**
055                         * Nothing to report.
056                         */
057                        IGNORED() {
058                                @Override
059                                public Level level() {
060                                        return Level.DEBUG;
061                                }
062                        },
063                        /**
064                         * OK.
065                         */
066                        OK() {
067                                @Override
068                                public Level level() {
069                                        return Level.INFO;
070                                }
071                        }
072
073                }
074
075                /**
076                 * Creates an error status of a throwable.
077                 * @param e throwable.
078                 * @return error status.
079                 */
080                static ErrorStatus ofError(Throwable e) {
081                        return ErrorStatus.of(e);
082                }
083
084                /**
085                 * Error status.
086                 *
087                 * @param message error message.
088                 */
089                record ErrorStatus(String message) implements Status {
090
091                        @Override
092                        public Level level() {
093                                return Level.ERROR;
094                        }
095
096                        static ErrorStatus of(ExecutionException e) {
097                                var cause = e.getCause();
098                                if (cause == null)
099                                        cause = e;
100                                return of(cause);
101                        }
102
103                        static ErrorStatus of(Throwable e) {
104                                String message = Objects.requireNonNullElse(e.getMessage(), "unknown error");
105                                return new Status.ErrorStatus(message);
106                        }
107                }
108
109                /**
110                 * A Status of Statuses. The severity is the status with the highest severity if
111                 * severity is not provided.
112                 *
113                 * @param status a list of statuses.
114                 * @param level severity.
115                 */
116                record AggregateStatus(List<Status> status, Level level) implements Status {
117
118                        /**
119                         * A Status of Statuses.
120                         * @param status a list of statuses.
121                         * @param level severity.
122                         */
123                        public AggregateStatus {
124                                status = List.copyOf(status);
125                        }
126
127                        /**
128                         * A Status of Statuses. The severity is the status with the highest severity.
129                         * @param status a list of statuses.
130                         */
131                        public AggregateStatus(List<Status> status) {
132                                this(status, level(status));
133                        }
134
135                        private static Level level(List<Status> status) {
136                                Level level = Level.ALL;
137                                for (var stat : status) {
138                                        var current = stat.level();
139                                        if (current.getSeverity() > level.getSeverity()) {
140                                                level = current;
141                                        }
142                                }
143                                return level;
144                        }
145                }
146
147                /**
148                 * A status that has metric information.
149                 */
150                sealed interface MetricStatus extends Status {
151
152                        @Override
153                        default Level level() {
154                                return Level.INFO;
155                        }
156
157                }
158
159                /**
160                 * A queue status for publishers.
161                 *
162                 * @param count current amount in queue.
163                 * @param max the maximum size of the queue.
164                 * @param level severity
165                 */
166                record QueueStatus(long count, long max, Level level) implements MetricStatus {
167
168                        /**
169                         * A queue status for publishers. The severity will be warning if queue count
170                         * is greater or equal to max.
171                         * @param count current amount in queue.
172                         * @param max the maximum size of the queue.
173                         */
174                        public QueueStatus(long count, long max) {
175                                this(count, max, level(count, max));
176                        }
177
178                        private static Level level(long count, long max) {
179                                if (count >= max) {
180                                        return Level.WARNING;
181                                }
182                                return Level.INFO;
183                        }
184                }
185
186        }
187
188}
189
190/**
191 * A marker interface for pluggable components in the logging system.
192 *
193 * @apiNote this interface is an internal detail.
194 */
195interface LogComponent {
196
197}
198
199record Response(Class<? extends LogComponent> type, String name, LogResponse.Status status) implements LogResponse {
200
201}