# 규약이 강제라고 적는 문장 MongoObservationConvention.java:1 package dev.caskeleton.adapter.outbound.mongo.observation; MongoObservationConvention.java:2 MongoObservationConvention.java:3 import java.util.Objects; MongoObservationConvention.java:4 import java.util.Set; MongoObservationConvention.java:5 MongoObservationConvention.java:6 /** MongoObservationConvention.java:7 * The only tags MongoDB telemetry may carry (design §27). MongoObservationConvention.java:8 * MongoObservationConvention.java:9 *

Two problems, one allowlist. High-cardinality tags — a document id, a tenant id, a query MongoObservationConvention.java:10 * parameter — multiply time series until the metrics backend either drops data or bills for it. And MongoObservationConvention.java:11 * the same values are the ones that turn a metrics store into an unmanaged copy of production data, MongoObservationConvention.java:12 * outside whatever retention and access rules the database is under. MongoObservationConvention.java:13 * MongoObservationConvention.java:14 *

Both are prevented the same way: a tag not on this list cannot be attached, so the mistake has MongoObservationConvention.java:15 * to be made in this file rather than at a call site. MongoObservationConvention.java:16 */ MongoObservationConvention.java:17 public final class MongoObservationConvention { MongoObservationConvention.java:18 MongoObservationConvention.java:19 private static final Set ALLOWED_TAGS = MongoObservationConvention.java:20 Set.of( MongoObservationConvention.java:21 "mongoProfile", MongoObservationConvention.java:22 "databaseProfile", MongoObservationConvention.java:23 "collectionProfile", MongoObservationConvention.java:24 "operationName", MongoObservationConvention.java:25 "operationType", MongoObservationConvention.java:26 "result", MongoObservationConvention.java:27 "failureCategory", MongoObservationConvention.java:28 "consistencyProfile"); MongoObservationConvention.java:29 MongoObservationConvention.java:30 private static final Set FORBIDDEN_TAGS = MongoObservationConvention.java:31 Set.of( MongoObservationConvention.java:32 "documentId", MongoObservationConvention.java:33 "rawTenantId", MongoObservationConvention.java:34 "tenantId", MongoObservationConvention.java:35 "dynamicCollectionName", MongoObservationConvention.java:36 "queryParameter", MongoObservationConvention.java:37 "query", MongoObservationConvention.java:38 "fullBson", MongoObservationConvention.java:39 "resumeToken", MongoObservationConvention.java:40 "shardKeyValue", MongoObservationConvention.java:41 "plaintextPII", MongoObservationConvention.java:42 "credential"); MongoObservationConvention.java:43 MongoObservationConvention.java:44 private MongoObservationConvention() {} MongoObservationConvention.java:45 MongoObservationConvention.java:46 /** The platform's standard convention. */ MongoObservationConvention.java:47 public static MongoObservationConvention standard() { MongoObservationConvention.java:48 return new MongoObservationConvention(); MongoObservationConvention.java:49 } MongoObservationConvention.java:50 MongoObservationConvention.java:51 /** The tag names telemetry may use. */ MongoObservationConvention.java:52 public Set allowedTagNames() { MongoObservationConvention.java:53 return ALLOWED_TAGS; MongoObservationConvention.java:54 } MongoObservationConvention.java:55 MongoObservationConvention.java:56 /** The tag names the design explicitly forbids, kept for assertions and review. */ MongoObservationConvention.java:57 public Set forbiddenTagNames() { # 그 강제를 수행하는 메서드 MongoObservationConvention.java:58 return FORBIDDEN_TAGS; MongoObservationConvention.java:59 } MongoObservationConvention.java:60 MongoObservationConvention.java:61 /** MongoObservationConvention.java:62 * Rejects a tag that is not on the allowlist. MongoObservationConvention.java:63 * MongoObservationConvention.java:64 * @throws IllegalArgumentException naming the rejected tag MongoObservationConvention.java:65 */ MongoObservationConvention.java:66 public void requireAllowed(String tagName) { MongoObservationConvention.java:67 Objects.requireNonNull(tagName, "tagName"); MongoObservationConvention.java:68 if (!ALLOWED_TAGS.contains(tagName)) { MongoObservationConvention.java:69 throw new IllegalArgumentException( MongoObservationConvention.java:70 "tag '" MongoObservationConvention.java:71 + tagName MongoObservationConvention.java:72 + "' is not on the MongoDB observation allowlist; the allowed tags are " MongoObservationConvention.java:73 + ALLOWED_TAGS); MongoObservationConvention.java:74 } MongoObservationConvention.java:75 } MongoObservationConvention.java:76 } # 그것을 부르는 자리 MongoObservationConvention 을 언급하는 main 파일 : 2 개 MongoDriverObservabilityConfiguration.java MongoObservationConvention.java 이 리프 main 에서 requireAllowed 를 부르는 줄 : 0 개 requireAllowed 가 이 리프에 나오는 자리 전부 : main · MongoSearchQuery.java:48 public void requireAllowedPaths(Set allowedPaths) { main · ShardAwareQueryValidator.java:59 public MongoRoutingClassification requireAllowedRead( main · MongoAggregationProfile.java:86 public void requireAllowed(MongoAggregationStageDescriptor descriptor) { main · PolicyAwareMongoAggregationExecutor.java:49 plan.stages().forEach(profile::requireAllowed); main · MongoObservationConvention.java:66 public void requireAllowed(String tagName) { test · MongoSearchReadinessGateTest.java:52 assertThatCode(() -> query.requireAllowedPaths(Set.of("customerName"))) test · MongoSearchReadinessGateTest.java:54 assertThatThrownBy(() -> query.requireAllowedPaths(Set.of("orderNumber"))) test · ShardAwareQueryValidatorTest.java:57 assertThatThrownBy(() -> validator.requireAllowedRead(key, Set.of("status"), false)) test · ShardAwareQueryValidatorTest.java:60 assertThatCode(() -> validator.requireAllowedRead(key, Set.of("status"), true)) test · PolicyAwareMongoAggregationExecutorTest.java:20 assertThatThrownBy(() -> profile.requireAllowed(MongoAggregationStageDescriptor.of("$merge"))) test · PolicyAwareMongoAggregationExecutorTest.java:29 .requireAllowed(MongoAggregationStageDescriptor.of("$out"))) test · PolicyAwareMongoAggregationExecutorTest.java:37 assertThatCode(() -> profile.requireAllowed(MongoAggregationStageDescriptor.of("$match"))) test · PolicyAwareMongoAggregationExecutorTest.java:39 assertThatCode(() -> profile.requireAllowed(MongoAggregationStageDescriptor.of("$project"))) test · PolicyAwareMongoAggregationExecutorTest.java:48 .requireAllowed(MongoAggregationStageDescriptor.of("$group"))) test · PolicyAwareMongoAggregationExecutorTest.java:55 .requireAllowed(MongoAggregationStageDescriptor.of("$group"))) test · PolicyAwareMongoAggregationExecutorTest.java:64 assertThatCode(() -> reviewed.requireAllowed(MongoAggregationStageDescriptor.of("$facet"))) test · PolicyAwareMongoAggregationExecutorTest.java:67 () -> reviewed.requireAllowed(MongoAggregationStageDescriptor.of("$graphLookup"))) test · MongoObservationConventionTest.java:38 assertThatCode(() -> convention.requireAllowed("operationName")).doesNotThrowAnyException(); test · MongoObservationConventionTest.java:39 assertThatThrownBy(() -> convention.requireAllowed("shardKeyValue")) test · MongoSecurityIntegrationLaneTest.java:163 assertThatThrownBy(() -> convention.requireAllowed("documentId")) test · MongoSecurityIntegrationLaneTest.java:165 assertThatThrownBy(() -> convention.requireAllowed("tenantId")) test · MongoSecurityIntegrationLaneTest.java:167 assertThatCode(() -> convention.requireAllowed("operationName")).doesNotThrowAnyException(); [대조] 저장소 전체 main 에서 그 이름을 부르는 줄 : 3 개 GraphQlRSocketAdmission.java:47 routePolicy.requireAllowed(route); BlockingRedirectCoordinator.java:93 targetGuard.requireAllowed(follow.target(), metadata); PublishPosterImageUseCase.java:106 policy.requireAllowed(command.contentIdentity(), command.mediaType()); [대조] 같은 pathspec 으로 Tags.of 를 세면 : 9 개 # 그 규약을 언급하는 다른 한 파일이 하는 일 MongoDriverObservabilityConfiguration.java:20 public MongoDriverObservabilityConfiguration(MeterRegistry registry, String mongoProfile) { MongoDriverObservabilityConfiguration.java:21 this.registry = Objects.requireNonNull(registry, "registry"); MongoDriverObservabilityConfiguration.java:22 this.mongoProfile = Objects.requireNonNull(mongoProfile, "mongoProfile"); MongoDriverObservabilityConfiguration.java:23 } MongoDriverObservabilityConfiguration.java:24 MongoDriverObservabilityConfiguration.java:25 /** Adds the command, SDAM and pool listeners to a client settings builder. */ MongoDriverObservabilityConfiguration.java:26 public MongoClientSettings.Builder apply(MongoClientSettings.Builder builder) { MongoDriverObservabilityConfiguration.java:27 Objects.requireNonNull(builder, "builder"); MongoDriverObservabilityConfiguration.java:28 builder.addCommandListener(new MongoCommandObservationListener(registry, mongoProfile)); MongoDriverObservabilityConfiguration.java:29 builder.applyToClusterSettings( MongoDriverObservabilityConfiguration.java:30 cluster -> MongoDriverObservabilityConfiguration.java:31 cluster.addClusterListener(new MongoSdamObservationListener(registry, mongoProfile))); MongoDriverObservabilityConfiguration.java:32 builder.applyToConnectionPoolSettings( MongoDriverObservabilityConfiguration.java:33 pool -> MongoDriverObservabilityConfiguration.java:34 pool.addConnectionPoolListener( MongoDriverObservabilityConfiguration.java:35 new MongoPoolObservationListener(registry, mongoProfile))); MongoDriverObservabilityConfiguration.java:36 return builder; MongoDriverObservabilityConfiguration.java:37 } MongoDriverObservabilityConfiguration.java:38 MongoDriverObservabilityConfiguration.java:39 /** The tag convention these listeners obey. */ MongoDriverObservabilityConfiguration.java:40 public MongoObservationConvention convention() { MongoDriverObservabilityConfiguration.java:41 return MongoObservationConvention.standard(); MongoDriverObservabilityConfiguration.java:42 } MongoDriverObservabilityConfiguration.java:43 } standard() 를 부르는 main 줄 : 1 개 convention() 을 부르는 main 줄 : 0 개 # 관측 클래스들이 실제로 태그를 붙이는 방법 이 패키지에서 Tags.of 를 부르는 줄 : 9 개 MicrometerMongoOperationObserver.java:46 Tags.of( MicrometerMongoOperationObserver.java:72 this.outcomeTags = Tags.of("result", "unknown", "failureCategory", "none"); MicrometerMongoOperationObserver.java:77 outcomeTags = Tags.of("result", outcome.name(), "failureCategory", "none"); MicrometerMongoOperationObserver.java:83 Tags.of( MongoCommandObservationListener.java:63 Tags.of( MongoPoolObservationListener.java:40 Tags tags = Tags.of("mongoProfile", mongoProfile); MongoPoolObservationListener.java:60 .tags(Tags.of("mongoProfile", mongoProfile, "failureCategory", event.getReason().name())) MongoSdamObservationListener.java:46 .tags(Tags.of("mongoProfile", mongoProfile)) MongoSdamObservationListener.java:61 .tags(Tags.of("mongoProfile", mongoProfile)) [대조] 저장소 전체에서 Tags.of 를 부르는 줄 : 30 개 # 편집기의 세 갈래 가운데 쓰이는 것 MongoObservationRedactor.java:18 /** Commands whose arguments are structural rather than data-bearing. */ MongoObservationRedactor.java:19 private static final Set DESCRIBABLE_COMMANDS = MongoObservationRedactor.java:20 Set.of("ping", "hello", "buildInfo", "listCollections", "listIndexes", "collStats"); MongoObservationRedactor.java:21 MongoObservationRedactor.java:22 /** Commands whose arguments always carry credentials or key material. */ MongoObservationRedactor.java:23 private static final Set ALWAYS_REDACTED = MongoObservationRedactor.java:24 Set.of( MongoObservationRedactor.java:25 "authenticate", MongoObservationRedactor.java:26 "saslStart", MongoObservationRedactor.java:27 "saslContinue", MongoObservationRedactor.java:28 "getnonce", MongoObservationRedactor.java:29 "createUser", MongoObservationRedactor.java:30 "updateUser", MongoObservationRedactor.java:31 "copydbgetnonce", MongoObservationRedactor.java:32 "copydbsaslstart", MongoObservationRedactor.java:33 "copydb"); MongoObservationRedactor.java:34 MongoObservationRedactor.java:35 /** The redacted rendering of a command. */ MongoObservationRedactor.java:36 public String describe(String commandName) { MongoObservationRedactor.java:37 Objects.requireNonNull(commandName, "commandName"); MongoObservationRedactor.java:38 if (ALWAYS_REDACTED.contains(commandName)) { MongoObservationRedactor.java:39 return ""; MongoObservationRedactor.java:40 } MongoObservationRedactor.java:41 return DESCRIBABLE_COMMANDS.contains(commandName) ? commandName : commandName + "(...)"; MongoObservationRedactor.java:42 } MongoObservationRedactor.java:43 MongoObservationRedactor.java:44 /** True when a command's arguments must never be described. */ MongoObservationRedactor.java:45 public boolean isAlwaysRedacted(String commandName) { MongoObservationRedactor.java:46 return ALWAYS_REDACTED.contains(Objects.requireNonNull(commandName, "commandName")); MongoObservationRedactor.java:47 } MongoObservationRedactor.java:48 } redactor.describe 를 부르는 main 줄 : 0 개 redactor.isAlwaysRedacted 를 부르는 main 줄 : 1 개 [대조] 시험에서 redactor.describe 는 : 6 개 리스너가 그 둘 중 무엇을 쓰는가 : MongoCommandObservationListener.java:59 private void record(String commandName, String result, long elapsedNanos) { MongoCommandObservationListener.java:60 registry MongoCommandObservationListener.java:61 .timer( MongoCommandObservationListener.java:62 METRIC_NAME, MongoCommandObservationListener.java:63 Tags.of( MongoCommandObservationListener.java:64 "mongoProfile", MongoCommandObservationListener.java:65 mongoProfile, MongoCommandObservationListener.java:66 "operationType", MongoCommandObservationListener.java:67 redactor.isAlwaysRedacted(commandName) ? "" : commandName, MongoCommandObservationListener.java:68 "result", MongoCommandObservationListener.java:69 result)) MongoCommandObservationListener.java:70 .record(elapsedNanos, TimeUnit.NANOSECONDS); MongoCommandObservationListener.java:71 } MongoCommandObservationListener.java:72 }