001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.juneau.rest.server.observation;
018
019/**
020 * Handle to one in-flight custom (non-request) observation.
021 *
022 * <p>
023 * Returned by {@link Observer#start(String, String)} and by the {@link Observations} facade. An
024 * observation pairs a metric timer with a tracing span around an arbitrary block of work that has no
025 * associated HTTP request &mdash; the request-path equivalent is the framework-driven boundary in
026 * {@code RestOpInvoker}.
027 *
028 * <h5 class='topic'>Usage</h5>
029 *
030 * <p>
031 * The handle is {@link AutoCloseable} and is designed for try-with-resources. On {@link #close()} it
032 * records the elapsed time to the metrics recorder and ends the span. Mark a failure with
033 * {@link #setError(Throwable)} before the block exits so both the timer's {@code exception} tag and the
034 * span's error status reflect it:
035 *
036 * <p class='bjava'>
037 *    <jk>try</jk> (Observation <jv>o</jv> = Observations.<jsm>observe</jsm>(<js>"order.load"</js>, <js>"team=payments"</js>)) {
038 *       <jk>return</jk> loadOrder(<jv>id</jv>);
039 *    } <jk>catch</jk> (RuntimeException <jv>e</jv>) {
040 *       <jv>o</jv>.setError(<jv>e</jv>);
041 *       <jk>throw</jk> <jv>e</jv>;
042 *    }
043 * </p>
044 *
045 * <h5 class='topic'>No-backend contract</h5>
046 *
047 * <p>
048 * When no metrics recorder / tracer is installed, {@link Observations} hands back
049 * {@link #NOOP} &mdash; a process-wide singleton whose methods do nothing and whose timing is never
050 * taken, so an observation in an unconfigured process costs nothing beyond the try-with-resources
051 * scaffolding.
052 *
053 * @since 10.0.0
054 */
055@SuppressWarnings({
056   "resource" // NOOP is an intentional stateless no-op singleton held in a static field; its close() is empty, so there is nothing to leak.
057})
058public interface Observation extends AutoCloseable {
059
060   /**
061    * Process-wide no-op observation, returned whenever no backend is installed.
062    *
063    * <p>
064    * Drops the error on the floor and does nothing on close.
065    */
066   Observation NOOP = new Observation() {
067      @Override /* Observation */
068      public void setError(Throwable error) {
069         // Intentionally empty.
070      }
071
072      @Override /* Observation */
073      public void close() {
074         // Intentionally empty.
075      }
076   };
077
078   /**
079    * Marks the observed block as failed.
080    *
081    * <p>
082    * The throwable is surfaced as the metric timer's {@code exception} tag and recorded on the tracing
083    * span (status {@code ERROR} + {@code exception.type} attribute) when the observation closes. Safe
084    * to call at most once; a {@code null} argument is treated as "no error".
085    *
086    * @param error The exception that caused the block to fail. May be <jk>null</jk> (treated as success).
087    */
088   void setError(Throwable error);
089
090   /**
091    * Closes the observation &mdash; records the elapsed timer sample and ends the span.
092    *
093    * <p>
094    * Always called via the try-with-resources {@code finally} contract. Implementations must not throw.
095    */
096   @Override /* AutoCloseable */
097   void close();
098}