contextPrefix ) { return parent::formatV0( $record ); } $context = !empty( $record['context'] ) ? $record['context'] : []; $record['context'] = []; $formatted = parent::formatV0( $record ); $formatted['@fields'] = $this->fixKeyConflicts( $formatted['@fields'], $context ); return $formatted; } /** * Prevent key conflicts * @param array $record * @return array */ protected function formatV1( array $record ) { if ( $this->contextPrefix ) { return parent::formatV1( $record ); } $context = !empty( $record['context'] ) ? $record['context'] : []; $record['context'] = []; $formatted = parent::formatV1( $record ); $formatted = $this->fixKeyConflicts( $formatted, $context ); return $formatted; } /** * Check whether some context field would overwrite another message key. If so, rename * and flag. * @param array $fields Fields to be sent to logstash * @param array $context Copy of the original $record['context'] * @return array Updated version of $fields */ protected function fixKeyConflicts( array $fields, array $context ) { foreach ( $context as $key => $val ) { if ( in_array( $key, $this->reservedKeys, true ) && isset( $fields[$key] ) && $fields[$key] !== $val ) { $fields['logstash_formatter_key_conflict'][] = $key; $key = 'c_' . $key; } $fields[$key] = $val; } return $fields; } /** * Use a more user-friendly trace format than NormalizerFormatter * @param \Exception|\Throwable $e * @return array */ protected function normalizeException( $e ) { if ( !$e instanceof \Exception && !$e instanceof \Throwable ) { throw new \InvalidArgumentException( 'Exception/Throwable expected, got ' . gettype( $e ) . ' / ' . get_class( $e ) ); } $data = [ 'class' => get_class( $e ), 'message' => $e->getMessage(), 'code' => $e->getCode(), 'file' => $e->getFile() . ':' . $e->getLine(), 'trace' => \MWExceptionHandler::getRedactedTraceAsString( $e ), ]; $previous = $e->getPrevious(); if ( $previous ) { $data['previous'] = $this->normalizeException( $previous ); } return $data; } }