ForgeOpsTracker.StrictPrivacy (forge_ops_tracker v0.18.0)

Copy Markdown View Source

Strict privacy (Configuration.t/0's strict_privacy) is for apps that can't let personal or health data leave their own servers: an event carries only structured facts (the error's type, its stack frames, release, environment, server, which route it happened in), never free text a person could have typed. The rules, and the vectors every client's tests check against, are in this repo's sdks/STRICT_PRIVACY.md; the server applies the same rules again on arrival (app/services/strict_privacy.rb) when the project's own Strict privacy setting is on.

Summary

Functions

An error event's payload (ForgeOpsTracker.EventBuilder.build/7's shape) reduced to what strict privacy sends: "message" is "" and "message_fingerprint" is added; breadcrumbs, endpoint, SQL statement and source context are dropped; a raw-path "transaction_name" keeps only its first segment (see transaction_name/1); context and tags keep only allowed_fields keys with plain values (left out when nothing is left); the user keeps only its id.

The first 32 hex characters of the SHA-256 of normalize_message/1's UTF-8 bytes: lets ForgeOps group errors whose messages differ only in ids, without the message being sent.

The message with hex object ids, UUIDs and standalone numbers replaced, in that order: "0x" plus 4 or more hex digits becomes "0x", a UUID becomes "<uuid>" and a run of digits becomes "<n>", the last two only where no ASCII word character touches them.

One finished span under strict privacy: its name per span_name/1, and its data reduced to "db.statement" and "db.system" (left out when neither is there).

A span's name under strict privacy: an outbound call keeps its method and host ("GET https://api.example.com/patients/42?x=1" is "GET api.example.com"), a request named by its path goes through strict_path/1 ("GET /patients/42/notes" is "GET /patients/:path"), so does a bare path ("/42/edit" is "/:id/:path"), and any other name ("OrdersController#create") is kept.

A request or page path under strict privacy, with no query or fragment. A route template ("/patients/:id/notes", "/orders/{id}") is kept as it is: its words are a developer's and its parameters are already placeholders. A raw path keeps only its first segment (":id" when it holds an ASCII digit or is longer than 40 characters), then "/:path" if there was more, since anything after it can name a person: "/patients/jane-doe/12" is "/patients/:path". No segments at all is "/".

A transaction name under strict privacy: a route or a controller action is kept, but a raw path keeps only its first segment, by the same rule as span_name/1. A blank name is returned as it is.

Functions

event(payload, allowed_fields)

@spec event(map(), [String.t()]) :: map()

An error event's payload (ForgeOpsTracker.EventBuilder.build/7's shape) reduced to what strict privacy sends: "message" is "" and "message_fingerprint" is added; breadcrumbs, endpoint, SQL statement and source context are dropped; a raw-path "transaction_name" keeps only its first segment (see transaction_name/1); context and tags keep only allowed_fields keys with plain values (left out when nothing is left); the user keeps only its id.

message_fingerprint(message)

@spec message_fingerprint(String.t() | nil) :: String.t()

The first 32 hex characters of the SHA-256 of normalize_message/1's UTF-8 bytes: lets ForgeOps group errors whose messages differ only in ids, without the message being sent.

normalize_message(message)

@spec normalize_message(String.t() | nil) :: String.t()

The message with hex object ids, UUIDs and standalone numbers replaced, in that order: "0x" plus 4 or more hex digits becomes "0x", a UUID becomes "<uuid>" and a run of digits becomes "<n>", the last two only where no ASCII word character touches them.

span(span)

@spec span(map()) :: map()

One finished span under strict privacy: its name per span_name/1, and its data reduced to "db.statement" and "db.system" (left out when neither is there).

span_name(name)

@spec span_name(String.t()) :: String.t()

A span's name under strict privacy: an outbound call keeps its method and host ("GET https://api.example.com/patients/42?x=1" is "GET api.example.com"), a request named by its path goes through strict_path/1 ("GET /patients/42/notes" is "GET /patients/:path"), so does a bare path ("/42/edit" is "/:id/:path"), and any other name ("OrdersController#create") is kept.

strict_path(path)

@spec strict_path(String.t() | nil) :: String.t()

A request or page path under strict privacy, with no query or fragment. A route template ("/patients/:id/notes", "/orders/{id}") is kept as it is: its words are a developer's and its parameters are already placeholders. A raw path keeps only its first segment (":id" when it holds an ASCII digit or is longer than 40 characters), then "/:path" if there was more, since anything after it can name a person: "/patients/jane-doe/12" is "/patients/:path". No segments at all is "/".

transaction_name(name)

@spec transaction_name(String.t() | nil) :: String.t() | nil

A transaction name under strict privacy: a route or a controller action is kept, but a raw path keeps only its first segment, by the same rule as span_name/1. A blank name is returned as it is.