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}