Skip to content

Publish metrics through the logger and rework the public surface. - #15

Merged
gustavofreze merged 1 commit into
mainfrom
metrics/embedded-metric-format
Aug 24, 2026
Merged

Publish metrics through the logger and rework the public surface.#15
gustavofreze merged 1 commit into
mainfrom
metrics/embedded-metric-format

Conversation

@gustavofreze

Copy link
Copy Markdown
Member

A logger that also measures. TelemetryLogger writes an entry and a metric on the same stream, never on the same line: an entry is narrative and carries a severity, a metric is a measurement and carries none, so a threshold raised to quiet the logs does not stop a series. It hangs the component and the correlation identifier on the metric itself, which is what stitches a series back to the entries around it. StreamLogger stays PSR-3 only, and there the method does not exist on the type.

The Metrics namespace holds what a measurement is regardless of where it is published, with MetricUnit carrying UCUM base units, and a namespace per backend holds the driver that renders it: Metrics\CloudWatch for the Amazon Embedded Metric Format, the only place that names AWS.

Three guards refuse what would corrupt a series or leak through it: a name carried by a field and by a dimension at once, a dimension declared unbounded, and a dimension covered by a redaction, whose value would reach the metric index where no redaction and no retention can follow it.

The public surface was reviewed as a whole, since the new types made the old inconsistencies visible:

  • StructuredLogger becomes StreamLogger, next to InMemoryLogger, so both loggers are named after where the record goes. The builder is reached by builder(), and create() now always returns the type it is on.
  • LogContext becomes Correlation, and withContext becomes withCorrelation, which frees the word context for the PSR-3 payload it already named.
  • Exceptions read as the invariant they guard: MalformedRedactionPattern, UnboundedDimension, DuplicateMetricIdentifier.
  • Redactions holds the whole concept: the Redaction contract, the Mask and Visibility vocabulary, the strategies named after a kind of data, and GenericRedaction for everything else. Visibility is new, and it is what the three separate classes for full mask, visible edges, and visible words were spelling out one class at a time.
  • The builders became the configuration value objects the loggers are built from, so no internal type appears in a public signature.

Six strategies were promoted from what the services repeat by hand: SecretRedaction, QueryStringRedaction, QueryParametersRedaction, FilterExpressionRedaction, BirthDateRedaction, and PostalCodeRedaction.

Two defects found on the way, both fixed with a test that fails without the fix. Field names are now matched by name and not by spelling, so the secret patterns finally cover an accessToken next to an access_token and an apiKey next to an api_key, which they never did. And the filter expression masks the operand of an =in= comparison, whose parenthesized list survived the pattern the services were running.

The Makefile runs the container as the calling user, so vendor, reports and the analysis cache stop landing in the bind mount owned by root.

Please follow the contributing guidelines.

Summary

What this pull request does.

Related issue

Closes #...

Checklist

  • Tests added or updated.
  • Documentation updated when applicable.
  • make review passes.
  • make tests passes.

A logger that also measures. TelemetryLogger writes an entry and a metric on
the same stream, never on the same line: an entry is narrative and carries a
severity, a metric is a measurement and carries none, so a threshold raised to
quiet the logs does not stop a series. It hangs the component and the
correlation identifier on the metric itself, which is what stitches a series
back to the entries around it. StreamLogger stays PSR-3 only, and there the
method does not exist on the type.

The Metrics namespace holds what a measurement is regardless of where it is
published, with MetricUnit carrying UCUM base units, and a namespace per
backend holds the driver that renders it: Metrics\CloudWatch for the Amazon
Embedded Metric Format, the only place that names AWS.

Three guards refuse what would corrupt a series or leak through it: a name
carried by a field and by a dimension at once, a dimension declared unbounded,
and a dimension covered by a redaction, whose value would reach the metric
index where no redaction and no retention can follow it.

The public surface was reviewed as a whole, since the new types made the old
inconsistencies visible:

- StructuredLogger becomes StreamLogger, next to InMemoryLogger, so both
  loggers are named after where the record goes. The builder is reached by
  builder(), and create() now always returns the type it is on.
- LogContext becomes Correlation, and withContext becomes withCorrelation,
  which frees the word context for the PSR-3 payload it already named.
- Exceptions read as the invariant they guard: MalformedRedactionPattern,
  UnboundedDimension, DuplicateMetricIdentifier.
- Redactions holds the whole concept: the Redaction contract, the Mask and
  Visibility vocabulary, the strategies named after a kind of data, and
  GenericRedaction for everything else. Visibility is new, and it is what the
  three separate classes for full mask, visible edges, and visible words were
  spelling out one class at a time.
- The builders became the configuration value objects the loggers are built
  from, so no internal type appears in a public signature.

Six strategies were promoted from what the services repeat by hand:
SecretRedaction, QueryStringRedaction, QueryParametersRedaction,
FilterExpressionRedaction, BirthDateRedaction, and PostalCodeRedaction.

Two defects found on the way, both fixed with a test that fails without the
fix. Field names are now matched by name and not by spelling, so the secret
patterns finally cover an accessToken next to an access_token and an apiKey
next to an api_key, which they never did. And the filter expression masks the
operand of an =in= comparison, whose parenthesized list survived the pattern
the services were running.

The Makefile runs the container as the calling user, so vendor, reports and
the analysis cache stop landing in the bind mount owned by root.
@gustavofreze
gustavofreze merged commit f1ee712 into main Aug 24, 2026
5 checks passed
@gustavofreze
gustavofreze deleted the metrics/embedded-metric-format branch August 24, 2026 21:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant