*/ protected array $sensitiveFields = [ 'id_card', 'id_card_encrypted', 'id_card_hash', 'phone', 'email', 'password', 'token', 'secret', ]; /** * 处理日志记录,对敏感信息进行脱敏 * * @param array $record Monolog 2.x 格式的日志记录数组 * @return array 处理后的日志记录数组 */ public function __invoke(array $record): array { // 处理 context 中的敏感信息 if (isset($record['context']) && is_array($record['context'])) { $record['context'] = $this->maskArray($record['context']); } // 处理 extra 中的敏感信息 if (isset($record['extra']) && is_array($record['extra'])) { $record['extra'] = $this->maskArray($record['extra']); } // 对消息本身也进行脱敏(如果包含敏感信息) if (isset($record['message']) && is_string($record['message'])) { $record['message'] = $this->maskString($record['message']); } return $record; } /** * 脱敏数组中的敏感字段 * * @param array $data * @return array */ protected function maskArray(array $data): array { $masked = []; foreach ($data as $key => $value) { $lowerKey = strtolower($key); // 检查字段名是否包含敏感关键词 $isSensitive = false; foreach ($this->sensitiveFields as $field) { if (strpos($lowerKey, $field) !== false) { $isSensitive = true; break; } } if ($isSensitive && is_string($value)) { // 根据字段类型选择脱敏方法 if (strpos($lowerKey, 'phone') !== false) { $masked[$key] = DataMaskingHelper::maskPhone($value); } elseif (strpos($lowerKey, 'email') !== false) { $masked[$key] = DataMaskingHelper::maskEmail($value); } elseif (strpos($lowerKey, 'id_card') !== false) { $masked[$key] = DataMaskingHelper::maskIdCard($value); } else { // 其他敏感字段,用*替代 $masked[$key] = str_repeat('*', min(strlen($value), 20)); } } elseif (is_array($value)) { $masked[$key] = $this->maskArray($value); } else { $masked[$key] = $value; } } return $masked; } /** * 脱敏字符串中的敏感信息(简单模式,匹配常见格式) * * @param string $message * @return string */ protected function maskString(string $message): string { // 匹配身份证号(18位或15位数字) $message = preg_replace_callback( '/\b\d{15}(\d{3})?[Xx]?\b/', function ($matches) { return DataMaskingHelper::maskIdCard($matches[0]); }, $message ); // 匹配手机号(11位数字,1开头) $message = preg_replace_callback( '/\b1[3-9]\d{9}\b/', function ($matches) { return DataMaskingHelper::maskPhone($matches[0]); }, $message ); // 匹配邮箱 $message = preg_replace_callback( '/\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/', function ($matches) { return DataMaskingHelper::maskEmail($matches[0]); }, $message ); return $message; } }