001/* 002 * Copyright 2022-2026 Revetware LLC. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package com.soklet.otel; 018 019import org.jspecify.annotations.NonNull; 020import org.jspecify.annotations.Nullable; 021 022import javax.annotation.concurrent.NotThreadSafe; 023import javax.annotation.concurrent.ThreadSafe; 024import java.util.Objects; 025 026import static java.lang.String.format; 027import static java.util.Objects.requireNonNull; 028 029/** 030 * Controls which Soklet lifecycle events are converted into OpenTelemetry spans or span events. 031 * 032 * @author <a href="https://www.revetkn.com">Mark Allen</a> 033 */ 034@ThreadSafe 035public final class SpanPolicy { 036 @NonNull 037 private static final SpanPolicy DEFAULT_INSTANCE; 038 039 static { 040 DEFAULT_INSTANCE = builder().build(); 041 } 042 043 @NonNull 044 private final Boolean recordHttpRequestSpans; 045 @NonNull 046 private final Boolean recordStreamingResponseSpans; 047 @NonNull 048 private final Boolean recordSseConnectionSpans; 049 @NonNull 050 private final Boolean recordSseWriteEvents; 051 @NonNull 052 private final Boolean recordMcpRequestSpans; 053 @NonNull 054 private final Boolean recordMcpSessionEvents; 055 @NonNull 056 private final Boolean recordMcpSseStreamSpans; 057 @NonNull 058 private final Boolean recordClientAddress; 059 @NonNull 060 private final Boolean recordRequestId; 061 062 @NonNull 063 public static Builder builder() { 064 return new Builder(); 065 } 066 067 @NonNull 068 public static SpanPolicy defaultInstance() { 069 return DEFAULT_INSTANCE; 070 } 071 072 private SpanPolicy(@NonNull Builder builder) { 073 requireNonNull(builder); 074 075 this.recordHttpRequestSpans = builder.recordHttpRequestSpans; 076 this.recordStreamingResponseSpans = builder.recordStreamingResponseSpans; 077 this.recordSseConnectionSpans = builder.recordSseConnectionSpans; 078 this.recordSseWriteEvents = builder.recordSseWriteEvents; 079 this.recordMcpRequestSpans = builder.recordMcpRequestSpans; 080 this.recordMcpSessionEvents = builder.recordMcpSessionEvents; 081 this.recordMcpSseStreamSpans = builder.recordMcpSseStreamSpans; 082 this.recordClientAddress = builder.recordClientAddress; 083 this.recordRequestId = builder.recordRequestId; 084 } 085 086 @NonNull 087 public Boolean recordHttpRequestSpans() { 088 return this.recordHttpRequestSpans; 089 } 090 091 @NonNull 092 public Boolean recordStreamingResponseSpans() { 093 return this.recordStreamingResponseSpans; 094 } 095 096 @NonNull 097 public Boolean recordSseConnectionSpans() { 098 return this.recordSseConnectionSpans; 099 } 100 101 @NonNull 102 public Boolean recordSseWriteEvents() { 103 return this.recordSseWriteEvents; 104 } 105 106 @NonNull 107 public Boolean recordMcpRequestSpans() { 108 return this.recordMcpRequestSpans; 109 } 110 111 @NonNull 112 public Boolean recordMcpSessionEvents() { 113 return this.recordMcpSessionEvents; 114 } 115 116 @NonNull 117 public Boolean recordMcpSseStreamSpans() { 118 return this.recordMcpSseStreamSpans; 119 } 120 121 @NonNull 122 public Boolean recordClientAddress() { 123 return this.recordClientAddress; 124 } 125 126 @NonNull 127 public Boolean recordRequestId() { 128 return this.recordRequestId; 129 } 130 131 @Override 132 @NonNull 133 public String toString() { 134 return format("%s{recordHttpRequestSpans=%s, recordStreamingResponseSpans=%s, recordSseConnectionSpans=%s, recordSseWriteEvents=%s, recordMcpRequestSpans=%s, recordMcpSessionEvents=%s, recordMcpSseStreamSpans=%s, recordClientAddress=%s, recordRequestId=%s}", 135 getClass().getSimpleName(), recordHttpRequestSpans(), recordStreamingResponseSpans(), recordSseConnectionSpans(), 136 recordSseWriteEvents(), recordMcpRequestSpans(), recordMcpSessionEvents(), 137 recordMcpSseStreamSpans(), recordClientAddress(), recordRequestId()); 138 } 139 140 @Override 141 public boolean equals(@Nullable Object object) { 142 if (this == object) 143 return true; 144 145 if (!(object instanceof SpanPolicy spanPolicy)) 146 return false; 147 148 return Objects.equals(recordHttpRequestSpans(), spanPolicy.recordHttpRequestSpans()) 149 && Objects.equals(recordStreamingResponseSpans(), spanPolicy.recordStreamingResponseSpans()) 150 && Objects.equals(recordSseConnectionSpans(), spanPolicy.recordSseConnectionSpans()) 151 && Objects.equals(recordSseWriteEvents(), spanPolicy.recordSseWriteEvents()) 152 && Objects.equals(recordMcpRequestSpans(), spanPolicy.recordMcpRequestSpans()) 153 && Objects.equals(recordMcpSessionEvents(), spanPolicy.recordMcpSessionEvents()) 154 && Objects.equals(recordMcpSseStreamSpans(), spanPolicy.recordMcpSseStreamSpans()) 155 && Objects.equals(recordClientAddress(), spanPolicy.recordClientAddress()) 156 && Objects.equals(recordRequestId(), spanPolicy.recordRequestId()); 157 } 158 159 @Override 160 public int hashCode() { 161 return Objects.hash(recordHttpRequestSpans(), recordStreamingResponseSpans(), recordSseConnectionSpans(), 162 recordSseWriteEvents(), recordMcpRequestSpans(), recordMcpSessionEvents(), 163 recordMcpSseStreamSpans(), recordClientAddress(), recordRequestId()); 164 } 165 166 /** 167 * Mutable builder for {@link SpanPolicy} instances. 168 */ 169 @NotThreadSafe 170 public static final class Builder { 171 @NonNull 172 private Boolean recordHttpRequestSpans; 173 @NonNull 174 private Boolean recordStreamingResponseSpans; 175 @NonNull 176 private Boolean recordSseConnectionSpans; 177 @NonNull 178 private Boolean recordSseWriteEvents; 179 @NonNull 180 private Boolean recordMcpRequestSpans; 181 @NonNull 182 private Boolean recordMcpSessionEvents; 183 @NonNull 184 private Boolean recordMcpSseStreamSpans; 185 @NonNull 186 private Boolean recordClientAddress; 187 @NonNull 188 private Boolean recordRequestId; 189 190 private Builder() { 191 this.recordHttpRequestSpans = true; 192 this.recordStreamingResponseSpans = true; 193 this.recordSseConnectionSpans = true; 194 this.recordSseWriteEvents = false; 195 this.recordMcpRequestSpans = true; 196 this.recordMcpSessionEvents = true; 197 this.recordMcpSseStreamSpans = true; 198 this.recordClientAddress = false; 199 this.recordRequestId = false; 200 } 201 202 @NonNull 203 public Builder recordHttpRequestSpans(@NonNull Boolean recordHttpRequestSpans) { 204 this.recordHttpRequestSpans = requireNonNull(recordHttpRequestSpans); 205 return this; 206 } 207 208 @NonNull 209 public Builder recordStreamingResponseSpans(@NonNull Boolean recordStreamingResponseSpans) { 210 this.recordStreamingResponseSpans = requireNonNull(recordStreamingResponseSpans); 211 return this; 212 } 213 214 @NonNull 215 public Builder recordSseConnectionSpans(@NonNull Boolean recordSseConnectionSpans) { 216 this.recordSseConnectionSpans = requireNonNull(recordSseConnectionSpans); 217 return this; 218 } 219 220 @NonNull 221 public Builder recordSseWriteEvents(@NonNull Boolean recordSseWriteEvents) { 222 this.recordSseWriteEvents = requireNonNull(recordSseWriteEvents); 223 return this; 224 } 225 226 @NonNull 227 public Builder recordMcpRequestSpans(@NonNull Boolean recordMcpRequestSpans) { 228 this.recordMcpRequestSpans = requireNonNull(recordMcpRequestSpans); 229 return this; 230 } 231 232 @NonNull 233 public Builder recordMcpSessionEvents(@NonNull Boolean recordMcpSessionEvents) { 234 this.recordMcpSessionEvents = requireNonNull(recordMcpSessionEvents); 235 return this; 236 } 237 238 @NonNull 239 public Builder recordMcpSseStreamSpans(@NonNull Boolean recordMcpSseStreamSpans) { 240 this.recordMcpSseStreamSpans = requireNonNull(recordMcpSseStreamSpans); 241 return this; 242 } 243 244 @NonNull 245 public Builder recordClientAddress(@NonNull Boolean recordClientAddress) { 246 this.recordClientAddress = requireNonNull(recordClientAddress); 247 return this; 248 } 249 250 @NonNull 251 public Builder recordRequestId(@NonNull Boolean recordRequestId) { 252 this.recordRequestId = requireNonNull(recordRequestId); 253 return this; 254 } 255 256 @NonNull 257 public SpanPolicy build() { 258 return new SpanPolicy(this); 259 } 260 } 261}