NormalizeNestedJSONForNoise rewrites a JSON string so that any string fields which themselves contain JSON are parsed into real JSON objects/arrays. It only does this for fields that are hinted by the noise configuration, i.e. when noise keys contain dotted paths like "json_response.timestamp". Thi
(raw string, noise map[string][]string, log *zap.Logger)
| 1740 | // If raw is not JSON, noise is empty, or any error occurs, it returns raw |
| 1741 | // unchanged so it is safe to call in all conditions. |
| 1742 | func NormalizeNestedJSONForNoise(raw string, noise map[string][]string, log *zap.Logger) string { |
| 1743 | if raw == "" || len(noise) == 0 { |
| 1744 | return raw |
| 1745 | } |
| 1746 | // Fast rejection: not JSON at all → nothing to normalize |
| 1747 | if !json.Valid([]byte(raw)) { |
| 1748 | return raw |
| 1749 | } |
| 1750 | |
| 1751 | // Collect "root" keys from noise that indicate nested paths. |
| 1752 | // Example: "json_response.timestamp" → root "json_response". |
| 1753 | rootKeys := make(map[string]bool) |
| 1754 | for k := range noise { |
| 1755 | parts := strings.SplitN(k, ".", 2) |
| 1756 | if len(parts) > 1 { |
| 1757 | root := strings.ToLower(parts[0]) |
| 1758 | rootKeys[root] = true |
| 1759 | } |
| 1760 | } |
| 1761 | if len(rootKeys) == 0 { |
| 1762 | // No dotted paths → no need to rewrite nested JSON |
| 1763 | return raw |
| 1764 | } |
| 1765 | |
| 1766 | var v interface{} |
| 1767 | if err := json.Unmarshal([]byte(raw), &v); err != nil { |
| 1768 | utils.LogError(log, err, "failed to unmarshal decoded gRPC data for normalization") |
| 1769 | return raw |
| 1770 | } |
| 1771 | |
| 1772 | normalizeNestedJSONValue(v, rootKeys) |
| 1773 | |
| 1774 | buf, err := json.Marshal(v) |
| 1775 | if err != nil { |
| 1776 | utils.LogError(log, err, "failed to re-marshal decoded gRPC data after normalization") |
| 1777 | return raw |
| 1778 | } |
| 1779 | return string(buf) |
| 1780 | } |
| 1781 | |
| 1782 | // normalizeNestedJSONValue walks the parsed JSON value and replaces any |
| 1783 | // string fields whose key is in rootKeys and whose value is itself valid |