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 — 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} — 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 — 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}