1 /* 2 * Copyright 2016-17, OpenCensus Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package io.opencensus.trace; 18 19 import io.opencensus.internal.Utils; 20 import java.util.ArrayList; 21 import java.util.Arrays; 22 import java.util.Collections; 23 import java.util.List; 24 import java.util.TreeMap; 25 import javax.annotation.Nullable; 26 import javax.annotation.concurrent.Immutable; 27 28 /*>>> 29 import org.checkerframework.dataflow.qual.Deterministic; 30 */ 31 32 /** 33 * Defines the status of a {@link Span} by providing a standard {@link CanonicalCode} in conjunction 34 * with an optional descriptive message. Instances of {@code Status} are created by starting with 35 * the template for the appropriate {@link Status.CanonicalCode} and supplementing it with 36 * additional information: {@code Status.NOT_FOUND.withDescription("Could not find 37 * 'important_file.txt'");} 38 * 39 * @since 0.5 40 */ 41 @Immutable 42 public final class Status { 43 /** 44 * The set of canonical status codes. If new codes are added over time they must choose a 45 * numerical value that does not collide with any previously used value. 46 * 47 * @since 0.5 48 */ 49 public enum CanonicalCode { 50 /** 51 * The operation completed successfully. 52 * 53 * @since 0.5 54 */ 55 OK(0), 56 57 /** 58 * The operation was cancelled (typically by the caller). 59 * 60 * @since 0.5 61 */ 62 CANCELLED(1), 63 64 /** 65 * Unknown error. An example of where this error may be returned is if a Status value received 66 * from another address space belongs to an error-space that is not known in this address space. 67 * Also errors raised by APIs that do not return enough error information may be converted to 68 * this error. 69 * 70 * @since 0.5 71 */ 72 UNKNOWN(2), 73 74 /** 75 * Client specified an invalid argument. Note that this differs from FAILED_PRECONDITION. 76 * INVALID_ARGUMENT indicates arguments that are problematic regardless of the state of the 77 * system (e.g., a malformed file name). 78 * 79 * @since 0.5 80 */ 81 INVALID_ARGUMENT(3), 82 83 /** 84 * Deadline expired before operation could complete. For operations that change the state of the 85 * system, this error may be returned even if the operation has completed successfully. For 86 * example, a successful response from a server could have been delayed long enough for the 87 * deadline to expire. 88 * 89 * @since 0.5 90 */ 91 DEADLINE_EXCEEDED(4), 92 93 /** 94 * Some requested entity (e.g., file or directory) was not found. 95 * 96 * @since 0.5 97 */ 98 NOT_FOUND(5), 99 100 /** 101 * Some entity that we attempted to create (e.g., file or directory) already exists. 102 * 103 * @since 0.5 104 */ 105 ALREADY_EXISTS(6), 106 107 /** 108 * The caller does not have permission to execute the specified operation. PERMISSION_DENIED 109 * must not be used for rejections caused by exhausting some resource (use RESOURCE_EXHAUSTED 110 * instead for those errors). PERMISSION_DENIED must not be used if the caller cannot be 111 * identified (use UNAUTHENTICATED instead for those errors). 112 * 113 * @since 0.5 114 */ 115 PERMISSION_DENIED(7), 116 117 /** 118 * Some resource has been exhausted, perhaps a per-user quota, or perhaps the entire file system 119 * is out of space. 120 * 121 * @since 0.5 122 */ 123 RESOURCE_EXHAUSTED(8), 124 125 /** 126 * Operation was rejected because the system is not in a state required for the operation's 127 * execution. For example, directory to be deleted may be non-empty, an rmdir operation is 128 * applied to a non-directory, etc. 129 * 130 * <p>A litmus test that may help a service implementor in deciding between FAILED_PRECONDITION, 131 * ABORTED, and UNAVAILABLE: (a) Use UNAVAILABLE if the client can retry just the failing call. 132 * (b) Use ABORTED if the client should retry at a higher-level (e.g., restarting a 133 * read-modify-write sequence). (c) Use FAILED_PRECONDITION if the client should not retry until 134 * the system state has been explicitly fixed. E.g., if an "rmdir" fails because the directory 135 * is non-empty, FAILED_PRECONDITION should be returned since the client should not retry unless 136 * they have first fixed up the directory by deleting files from it. 137 * 138 * @since 0.5 139 */ 140 FAILED_PRECONDITION(9), 141 142 /** 143 * The operation was aborted, typically due to a concurrency issue like sequencer check 144 * failures, transaction aborts, etc. 145 * 146 * <p>See litmus test above for deciding between FAILED_PRECONDITION, ABORTED, and UNAVAILABLE. 147 * 148 * @since 0.5 149 */ 150 ABORTED(10), 151 152 /** 153 * Operation was attempted past the valid range. E.g., seeking or reading past end of file. 154 * 155 * <p>Unlike INVALID_ARGUMENT, this error indicates a problem that may be fixed if the system 156 * state changes. For example, a 32-bit file system will generate INVALID_ARGUMENT if asked to 157 * read at an offset that is not in the range [0,2^32-1], but it will generate OUT_OF_RANGE if 158 * asked to read from an offset past the current file size. 159 * 160 * <p>There is a fair bit of overlap between FAILED_PRECONDITION and OUT_OF_RANGE. We recommend 161 * using OUT_OF_RANGE (the more specific error) when it applies so that callers who are 162 * iterating through a space can easily look for an OUT_OF_RANGE error to detect when they are 163 * done. 164 * 165 * @since 0.5 166 */ 167 OUT_OF_RANGE(11), 168 169 /** 170 * Operation is not implemented or not supported/enabled in this service. 171 * 172 * @since 0.5 173 */ 174 UNIMPLEMENTED(12), 175 176 /** 177 * Internal errors. Means some invariants expected by underlying system has been broken. If you 178 * see one of these errors, something is very broken. 179 * 180 * @since 0.5 181 */ 182 INTERNAL(13), 183 184 /** 185 * The service is currently unavailable. This is a most likely a transient condition and may be 186 * corrected by retrying with a backoff. 187 * 188 * <p>See litmus test above for deciding between FAILED_PRECONDITION, ABORTED, and UNAVAILABLE. 189 * 190 * @since 0.5 191 */ 192 UNAVAILABLE(14), 193 194 /** 195 * Unrecoverable data loss or corruption. 196 * 197 * @since 0.5 198 */ 199 DATA_LOSS(15), 200 201 /** 202 * The request does not have valid authentication credentials for the operation. 203 * 204 * @since 0.5 205 */ 206 UNAUTHENTICATED(16); 207 208 private final int value; 209 CanonicalCode(int value)210 private CanonicalCode(int value) { 211 this.value = value; 212 } 213 214 /** 215 * Returns the numerical value of the code. 216 * 217 * @return the numerical value of the code. 218 * @since 0.5 219 */ value()220 public int value() { 221 return value; 222 } 223 224 /** 225 * Returns the status that has the current {@code CanonicalCode}.. 226 * 227 * @return the status that has the current {@code CanonicalCode}. 228 * @since 0.5 229 */ toStatus()230 public Status toStatus() { 231 return STATUS_LIST.get(value); 232 } 233 } 234 235 // Create the canonical list of Status instances indexed by their code values. 236 private static final List<Status> STATUS_LIST = buildStatusList(); 237 buildStatusList()238 private static List<Status> buildStatusList() { 239 TreeMap<Integer, Status> canonicalizer = new TreeMap<Integer, Status>(); 240 for (CanonicalCode code : CanonicalCode.values()) { 241 Status replaced = canonicalizer.put(code.value(), new Status(code, null)); 242 if (replaced != null) { 243 throw new IllegalStateException( 244 "Code value duplication between " 245 + replaced.getCanonicalCode().name() 246 + " & " 247 + code.name()); 248 } 249 } 250 return Collections.unmodifiableList(new ArrayList<Status>(canonicalizer.values())); 251 } 252 253 // A pseudo-enum of Status instances mapped 1:1 with values in CanonicalCode. This simplifies 254 // construction patterns for derived instances of Status. 255 /** 256 * The operation completed successfully. 257 * 258 * @since 0.5 259 */ 260 public static final Status OK = CanonicalCode.OK.toStatus(); 261 262 /** 263 * The operation was cancelled (typically by the caller). 264 * 265 * @since 0.5 266 */ 267 public static final Status CANCELLED = CanonicalCode.CANCELLED.toStatus(); 268 269 /** 270 * Unknown error. See {@link CanonicalCode#UNKNOWN}. 271 * 272 * @since 0.5 273 */ 274 public static final Status UNKNOWN = CanonicalCode.UNKNOWN.toStatus(); 275 276 /** 277 * Client specified an invalid argument. See {@link CanonicalCode#INVALID_ARGUMENT}. 278 * 279 * @since 0.5 280 */ 281 public static final Status INVALID_ARGUMENT = CanonicalCode.INVALID_ARGUMENT.toStatus(); 282 283 /** 284 * Deadline expired before operation could complete. See {@link CanonicalCode#DEADLINE_EXCEEDED}. 285 * 286 * @since 0.5 287 */ 288 public static final Status DEADLINE_EXCEEDED = CanonicalCode.DEADLINE_EXCEEDED.toStatus(); 289 290 /** 291 * Some requested entity (e.g., file or directory) was not found. 292 * 293 * @since 0.5 294 */ 295 public static final Status NOT_FOUND = CanonicalCode.NOT_FOUND.toStatus(); 296 297 /** 298 * Some entity that we attempted to create (e.g., file or directory) already exists. 299 * 300 * @since 0.5 301 */ 302 public static final Status ALREADY_EXISTS = CanonicalCode.ALREADY_EXISTS.toStatus(); 303 304 /** 305 * The caller does not have permission to execute the specified operation. See {@link 306 * CanonicalCode#PERMISSION_DENIED}. 307 * 308 * @since 0.5 309 */ 310 public static final Status PERMISSION_DENIED = CanonicalCode.PERMISSION_DENIED.toStatus(); 311 312 /** 313 * The request does not have valid authentication credentials for the operation. 314 * 315 * @since 0.5 316 */ 317 public static final Status UNAUTHENTICATED = CanonicalCode.UNAUTHENTICATED.toStatus(); 318 319 /** 320 * Some resource has been exhausted, perhaps a per-user quota, or perhaps the entire file system 321 * is out of space. 322 * 323 * @since 0.5 324 */ 325 public static final Status RESOURCE_EXHAUSTED = CanonicalCode.RESOURCE_EXHAUSTED.toStatus(); 326 327 /** 328 * Operation was rejected because the system is not in a state required for the operation's 329 * execution. See {@link CanonicalCode#FAILED_PRECONDITION}. 330 * 331 * @since 0.5 332 */ 333 public static final Status FAILED_PRECONDITION = CanonicalCode.FAILED_PRECONDITION.toStatus(); 334 335 /** 336 * The operation was aborted, typically due to a concurrency issue like sequencer check failures, 337 * transaction aborts, etc. See {@link CanonicalCode#ABORTED}. 338 * 339 * @since 0.5 340 */ 341 public static final Status ABORTED = CanonicalCode.ABORTED.toStatus(); 342 343 /** 344 * Operation was attempted past the valid range. See {@link CanonicalCode#OUT_OF_RANGE}. 345 * 346 * @since 0.5 347 */ 348 public static final Status OUT_OF_RANGE = CanonicalCode.OUT_OF_RANGE.toStatus(); 349 350 /** 351 * Operation is not implemented or not supported/enabled in this service. 352 * 353 * @since 0.5 354 */ 355 public static final Status UNIMPLEMENTED = CanonicalCode.UNIMPLEMENTED.toStatus(); 356 357 /** 358 * Internal errors. See {@link CanonicalCode#INTERNAL}. 359 * 360 * @since 0.5 361 */ 362 public static final Status INTERNAL = CanonicalCode.INTERNAL.toStatus(); 363 364 /** 365 * The service is currently unavailable. See {@link CanonicalCode#UNAVAILABLE}. 366 * 367 * @since 0.5 368 */ 369 public static final Status UNAVAILABLE = CanonicalCode.UNAVAILABLE.toStatus(); 370 371 /** 372 * Unrecoverable data loss or corruption. 373 * 374 * @since 0.5 375 */ 376 public static final Status DATA_LOSS = CanonicalCode.DATA_LOSS.toStatus(); 377 378 // The canonical code of this message. 379 private final CanonicalCode canonicalCode; 380 381 // An additional error message. 382 @Nullable private final String description; 383 Status(CanonicalCode canonicalCode, @Nullable String description)384 private Status(CanonicalCode canonicalCode, @Nullable String description) { 385 this.canonicalCode = Utils.checkNotNull(canonicalCode, "canonicalCode"); 386 this.description = description; 387 } 388 389 /** 390 * Creates a derived instance of {@code Status} with the given description. 391 * 392 * @param description the new description of the {@code Status}. 393 * @return The newly created {@code Status} with the given description. 394 * @since 0.5 395 */ withDescription(@ullable String description)396 public Status withDescription(@Nullable String description) { 397 if (Utils.equalsObjects(this.description, description)) { 398 return this; 399 } 400 return new Status(this.canonicalCode, description); 401 } 402 403 /** 404 * Returns the canonical status code. 405 * 406 * @return the canonical status code. 407 * @since 0.5 408 */ getCanonicalCode()409 public CanonicalCode getCanonicalCode() { 410 return canonicalCode; 411 } 412 413 /** 414 * Returns the description of this {@code Status} for human consumption. 415 * 416 * @return the description of this {@code Status}. 417 * @since 0.5 418 */ 419 @Nullable 420 /*@Deterministic*/ getDescription()421 public String getDescription() { 422 return description; 423 } 424 425 /** 426 * Returns {@code true} if this {@code Status} is OK, i.e., not an error. 427 * 428 * @return {@code true} if this {@code Status} is OK. 429 * @since 0.5 430 */ isOk()431 public boolean isOk() { 432 return CanonicalCode.OK == canonicalCode; 433 } 434 435 /** 436 * Equality on Statuses is not well defined. Instead, do comparison based on their CanonicalCode 437 * with {@link #getCanonicalCode}. The description of the Status is unlikely to be stable, and 438 * additional fields may be added to Status in the future. 439 */ 440 @Override equals(@ullable Object obj)441 public boolean equals(@Nullable Object obj) { 442 if (obj == this) { 443 return true; 444 } 445 446 if (!(obj instanceof Status)) { 447 return false; 448 } 449 450 Status that = (Status) obj; 451 return canonicalCode == that.canonicalCode 452 && Utils.equalsObjects(description, that.description); 453 } 454 455 /** 456 * Hash codes on Statuses are not well defined. 457 * 458 * @see #equals 459 */ 460 @Override hashCode()461 public int hashCode() { 462 return Arrays.hashCode(new Object[] {canonicalCode, description}); 463 } 464 465 @Override toString()466 public String toString() { 467 return "Status{canonicalCode=" + canonicalCode + ", description=" + description + "}"; 468 } 469 } 470