• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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