# 규약이 강제라고 적는 문장
  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  * <p>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  * <p>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<String> 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<String> 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<String> 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<String> 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<String> 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<String> 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<String> 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 "<redacted>";
  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) ? "<redacted>" : commandName,
  MongoCommandObservationListener.java:68                 "result",
  MongoCommandObservationListener.java:69                 result))
  MongoCommandObservationListener.java:70         .record(elapsedNanos, TimeUnit.NANOSECONDS);
  MongoCommandObservationListener.java:71   }
  MongoCommandObservationListener.java:72 }
