Structured logs¶
TL;DR. OTel-aligned JSON on every line. Deploy / commit / pod / cloud region stamped automatically. PII masking on by default. Log → trace → metric pivots actually work.
JSON logs are table stakes. JSON logs that line up with what every other modern tool expects — that's what makes log → trace → metric pivoting actually work. Most teams either build this themselves (badly) or never get around to it and end up grepping plain text at 3 AM.
Pulse ships the layout, the deploy/commit/pod/region stamping, and the PII masking. Add the starter and every log line in your app — including the ones that fire before Spring is fully up — comes out in the right shape.
What you get¶
Every log line, including pre-Spring-Boot startup lines from background threads, looks like this:
{
"@timestamp": "2026-04-18T15:23:11.482Z",
"level": "INFO",
"message": "placing order",
"trace_id": "4c1f9b8a4e2d6f0b8c3a1e7d5b9a2c6f",
"span_id": "9b8a4e2d6f0b8c3a",
"service.name": "order-service",
"service.version": "1.4.2",
"deployment.environment": "prod",
"vcs.ref.head.revision": "8c3a1e7d5b9a2c6f4c1f9b8a4e2d6f0b8c3a1e7d",
"k8s.pod.name": "order-service-7d4b9c-fk2lp",
"k8s.namespace.name": "checkout",
"cloud.region": "us-east-1",
"cloud.availability_zone": "us-east-1a",
"http.request.id": "01HKGY9F7P3W2X4QV1B5KJN8MZ",
"user.id": "u-981234"
}
Same trace_id is on the matching span and on the matching metric — pivoting
between Loki, Jaeger, and Prometheus is now trace_id=… instead of an
internal investigation. One LogQL query answers questions like "slow
checkout requests on us-east-1a only" without per-app glue:
Turn it on¶
Nothing. The log layout, the field set, the resource discovery, and the PII masking are all on by default once you add the starter.
The only knob most people touch is the PII masking, and only to disable it in a sealed dev environment:
What it adds¶
The field set follows OpenTelemetry's semantic conventions so OTel-native sinks accept it without remapping:
| Field | Where it comes from |
|---|---|
trace_id, span_id |
The active OTel span (correlation across signals) |
service.name, service.version |
spring.application.name + build-info.properties |
deployment.environment |
app.env property |
vcs.ref.head.revision |
git.properties (or OTEL_RESOURCE_ATTRIBUTES, or GITHUB_SHA) |
host.name, container.id |
OS / cgroup detection at startup |
k8s.pod.name, k8s.namespace.name, k8s.node.name |
Kubernetes downward-API env vars |
cloud.provider, cloud.region, cloud.availability_zone |
AWS / GCP / Azure env vars |
http.request.id, user.id, pulse.tenant.id |
Pulse's request filter |
timeout_remaining_ms |
The active timeout budget |
All resource fields are resolved once at startup and cached, so there is no per-line cost.
PII masking¶
On by default. Redacts:
- Email addresses
- US Social Security numbers
- Credit-card numbers (Luhn-validated)
BearerandBasicAuthorization tokens- JSON properties named
password,secret,token,apikey(any case)
If you have domain-specific PII shapes, add patterns via standard Log4j2 plugin parameters.
When to skip it¶
You don't, really. The layout is JSON; if you don't want it, override
logging.config to point to your own file. Pulse only owns the field set
and the masking; everything else flows through standard Spring Boot
configuration.
The only legitimate reason to disable masking is "I'm running in a sealed dev environment with synthetic data and the masked outputs make debugging harder."
Logback users¶
Pulse defaults to Log4j2 (Spring Boot's higher-throughput logging backend) but supports Logback via opt-in — see the quick start for the dependency exclusion. The Logback path produces the exact same JSON shape as the Log4j2 path. Dashboards work unchanged either way.
Where do service.version and the git revision come from?¶
Pulse reads whichever source is present at runtime, in this order:
- JVM args (
-Dpulse.app.version=…) - Classpath
META-INF/build-info.properties+git.properties— the path most teams want, because the values travel inside the JAR through your registry and Docker image with no deploy-time configuration OTEL_RESOURCE_ATTRIBUTESenv var- Common CI env vars (
GITHUB_SHA,CI_COMMIT_SHA, …) - Boot JAR
Implementation-Version - Falls back to
"unknown"
To wire option 2, add the
spring-boot-maven-plugin build-info goal
and the
git-commit-id-maven-plugin
to your pom.xml. No code changes.
Source: log4j2-spring.xml ·
PiiMaskingConverter.java ·
Runbook: Trace context missing ·
Status: Stable since 1.0.0