# =====================================================================================================
# adapter-logging - complete reference configuration
#
# Every property of the SHARED adapter-logging namespace (both Legatium modules) with its DEFAULT value
# and the reasoning behind it. Copy the block into an application.yml and change only what you need:
# every value shown here is the built-in default, so an entry you leave out (or delete) behaves exactly
# like an entry you keep. Bound by ClientLoggingProperties; invalid values (blank names, non-positive
# sizes/durations) fail the context start with a message naming the property.
# =====================================================================================================

adapter-logging:
  # Master switch. `false` removes the module entirely - the auto-configuration backs off, no
  # interceptor/filter, no customizer, no beans. This is a context-start decision, not a runtime
  # toggle; for a runtime off-switch use the logger level below instead (a disabled level skips the
  # event assembly too).
  enabled: true

  # Logger the exchange events are emitted on. The level of THIS logger decides which calls are
  # visible: INFO shows every call, WARN keeps only failures, timeouts, cancellations and slow calls,
  # ERROR only calls that threw (see `logging.level` below). Deliberately distinct from the sibling
  # project limesium's `endpoint-http-exchange`, so inbound and outbound streams route independently.
  logger-name: adapter-http-exchange

  # Header the correlation id is read from on TRACELESS calls only (ADR-0002): an outgoing request with
  # a conformant W3C traceparent (put there by the host's tracing propagation) takes its request id from
  # the traceparent's trace id, ignores this header, and gets NOTHING added - the wire stays untouched.
  # On a traceless call a header already on the request is accepted; absent or blank means a new id is
  # generated (by default a 21-character counting id: random per-instance base-36 prefix plus counter,
  # ADR-0004 - override the CorrelationIdGenerator bean to change the format) AND SENT to the peer on
  # this same header name, so the peer can quote it. Whichever identity wins rides the MDC as
  # `adapter_request_id` while the call runs.
  correlation-id-header: X-Correlation-Id

  # Whether the query string is logged (as its own field `adapter_url_query`, never as part of the
  # path - grouping by path must not be defeated by varying queries). Disable when query parameters
  # may carry personal data.
  include-query-string: true

  # Optionally log an ARRIVAL line the moment the request is sent, before the wire call - useful when
  # long-running or hanging calls must be visible while still in flight. The arrival line carries no
  # outcome/status/duration, so dashboards keyed on `adapter_outcome` still count exactly one line per
  # call even with this enabled.
  log-request-start: false

  # URL patterns (Spring PathPattern syntax) that determine for which request PATHS the module is
  # active AT ALL, whatever the host. Empty (the default) means every call; a call is logged when it
  # matches ANY include pattern and NO exclude prefix and NO excluded host - an exclude always wins.
  # Invalid patterns fail the context start.
  include-path-patterns: []
  #  - /api/**

  # Request-path prefixes the module skips entirely - no event, no MDC, no correlation header. Prefix
  # match against the DECODED request path (percent-encoding resolved, path parameters dropped, so an
  # encoded variant cannot slip past). Default is empty (everything is logged):
  exclude-path-prefixes: []
  #  - /internal/

  # Peer hosts the module skips entirely - the outbound counterpart of excluding a health probe: a
  # metrics push gateway, a config server, a sidecar. Case-insensitive match against the request URI's
  # host (without port). Default is empty:
  exclude-hosts: []
  #  - pushgateway.monitoring.svc

  # At or above this duration the call escalates from INFO to WARN and is flagged `adapter_slow: true` -
  # the outcome stays `success`, slowness raises severity, it does not turn a completed call into a
  # failure. Duration is measured until the response is closed (response occupancy including the body
  # read), not bare round-trip time. Compared at millisecond resolution: values below 1ms are rejected
  # at startup.
  slow-request-threshold: 5s

  # Header logging, one section per direction, rendered into one display-only field each
  # (`adapter_request_headers` / `adapter_response_headers`). Matching is case-insensitive throughout,
  # as header names are. MASKED BY DEFAULT (ADR-0005): whatever the selection logs is rendered as a
  # fingerprint unless its name is explicitly allowed in plaintext - so the debugging move
  # `includes: ["*"]` costs readability, never confidentiality.
  request-headers:
    # Names to log. Empty (the default) logs NOTHING; the entry "*" logs every header the request
    # carries.
    includes: []
    #  - Accept
    #  - "*"
    # Names removed from the included set - meaningful mainly together with the "*" include; an
    # exclude always wins over an include.
    excludes: []
    #  - Cookie
    # Names whose VALUE is replaced by the HeaderValueMasker bean's rendering - by default a stable short
    # fingerprint (length:hex-hash, e.g. "18:a8b3c4d5"): a PSEUDONYM, not anonymisation - identical
    # values yield identical fingerprints, so a masked token can still be correlated across events
    # without exposing the secret, and a reader can still confirm a guessed value unless the fingerprint
    # is keyed with `masking-key` below. The default "*" masks every logged header; narrow it to names,
    # or empty it to switch masking off - a visible decision, never the side effect of another list.
    # Masking only affects headers that are logged at all - listing a name here does not include it.
    masked: ["*"]
    # Names that appear in PLAINTEXT although `masked` covers them - the allowlist of harmless names.
    # An unmasked name always wins over a masked one. No "*" here: the plaintext set is an explicit list
    # by design (to log everything in plaintext, empty `masked` instead).
    unmasked: []
    #  - Accept
    #  - X-Correlation-Id
  response-headers:
    # Same contract as request-headers, applied to the response.
    includes: []
    #  - Content-Type
    excludes: []
    masked: ["*"]
    unmasked: []
    #  - Content-Type

  # Body logging per direction - a MODE, not a switch (ADR-0006): `never` (the default), `on-failure`
  # or `always`. `on-failure` is the volume switch: the body is logged only when `adapter_outcome` is
  # not `success` (failure, timeout, cancelled) or the status is a 4xx - orders of magnitude fewer bytes
  # than `always`, on
  # exactly the lines a body is wanted for. The response side decides at emission, when the outcome is
  # final; the request body flows before the outcome is known, so `on-failure` buffers it (up to
  # max-body-bytes) like `always` and drops it on success - the capture is paid, the output is saved.
  # A 4xx answer keeps its `success` outcome (the peer answered) but logs its bodies in `on-failure`:
  # the client's error is exactly what the body explains.
  # The request body is what the client hands to the wire call (the complete serialized body, copied
  # BEFORE the call on the blocking stack - so a refused call still shows what was about to be sent;
  # teed as it is written to the connector on the reactive one); the response body is TEED as the application reads it - never
  # pre-read, never replayed, so streaming behaviour is untouched and the log shows exactly the bytes
  # that actually moved (a response body the application never read is logged as absent). Fields
  # `adapter_request_body` / `adapter_response_body` appear only when bytes flowed.
  log-request-body: never
  log-response-body: never

  # Body SIZE measurement per direction (meters `adapter.request.body.size` / `adapter.response.body.size`,
  # tagged by the URI template and the peer host). Deliberately independent of the log-*-body flags - a
  # metric must not appear and disappear with a logging flag. Measure-only installs a count-only
  # capture (nothing buffered); measured is what actually flowed, exact even beyond max-body-bytes.
  # Zero-byte bodies record no sample. On the blocking stack the REQUEST sample is recorded only for an
  # exchange that received a response - the interceptor copies the body before the wire call, and a
  # response is its one proof that the bytes went out. The response side additionally records the counter
  # `adapter.response.body.read` (tag `state` = unread | partial | complete): the tee mirrors what the
  # application CONSUMED, so this is the one signal that tells a body the peer sent but the application
  # dropped from a body that was never sent.
  # Cardinality precondition: the meters are tagged `uri` (the recorded URI template, kept only when it
  # carries a placeholder - `uri("/things/" + id)` folds to UNKNOWN) and `host` (the peer host as called,
  # NOT folded). A host that calls many peer hosts pays one tag set per host in its registry.
  measure-request-body-size: false
  measure-response-body-size: false

  # Capture limit per body, in bytes. Bounds MEMORY, not the exchange: bytes beyond the limit still
  # reach the peer respectively the application unchanged - only the logged value is truncated, with an
  # explicit note of the total size.
  max-body-bytes: 16384

  # Keys the masking fingerprint. Empty (the default) keeps the unkeyed length:hash fingerprint; any
  # other value turns it into an HMAC-SHA256 under this key - same shape, same stability under the
  # same key (so correlation across events, twins and the inbound sibling holds as long as they share
  # it), but a log reader without the key can no longer confirm a guessed value by hashing it. A
  # SECRET: supply it like one (environment variable, vault-backed property), never as a literal in a
  # checked-in file; the properties' toString redacts it. Ignored when a host pins its own
  # HeaderValueMasker bean.
  masking-key: ""

logging:
  level:
    # The exchange stream. INFO = every call (the default expectation); WARN = only failures, timeouts,
    # cancellations and slow calls; ERROR = only calls that threw. Severity and semantic are decoupled:
    # dashboards key on the `adapter_outcome` field, the level only decides how loud - and whether - a
    # line is emitted.
    adapter-http-exchange: INFO
    # The module's own logger: the immediate WARN breadcrumb when a call throws, and errors of the
    # logging itself. Keep at WARN or lower - silencing it hides the breadcrumb.
    eu.inqudium.legatium.restclient.logging: WARN
