# Copyright (c) 2022 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details. # frozen_string_literal: true require 'contrast/agent/telemetry/events/exceptions/telemetry_exceptions' module Contrast module Logger # Our decorator for the Ougai logger allowing for the catching, creating and saving Telemetry exceptions module AliasedLogging ALIASED_WARN = 'warn'.cs__freeze ALIASED_ERROR = 'error'.cs__freeze ALIASED_FATAL = 'fatal'.cs__freeze # @param message [String] The message to log. Use default_message if not specified. # @param exception [Exception] The exception or the error # @param data [Object] Any structured data def warn message = nil, exception = nil, data = nil, &block # build Telemetry Exclusion build_exclusion(ALIASED_WARN, message, exception, data) super(message, exception, data, &block) end # @param message [String] The message to log. Use default_message if not specified. # @param exception [Exception] The exception or the error # @param data [Object] Any structured data def error message = nil, exception = nil, data = nil, &block # build Telemetry Exclusion build_exclusion(ALIASED_ERROR, message, exception, data) super(message, exception, data, &block) end # @param message [String] The message to log. Use default_message if not specified. # @param exception [Exception] The exception or the error # @param data [Object] Any structured data def fatal message = nil, exception = nil, data = nil, &block # build Telemetry Exclusion build_exclusion(ALIASED_FATAL, message, exception, data) super(message, exception, data, &block) end private def build_exclusion type, message = nil, exception = nil, data = nil start = caller_locations&.find_index { |stack| stack.to_s.include?(type) } stack_trace = start ? caller_locations(start + 1, 20) : caller_locations(20, 20) stack_frame_type = stack_trace[1].path.delete_prefix(Dir.pwd) message_exception_type = exception ? exception.cs__class.to_s : stack_frame_type.split('/').last stack_frame_function = stack_trace[1].label key = "#{ stack_frame_type }|#{ stack_frame_function }|#{ message }" if TELEMETRY_EXCEPTIONS[key] TELEMETRY_EXCEPTIONS.increment key return end event_message = create_message(stack_frame_function, stack_frame_type, message_exception_type, data, exception, message) TELEMETRY_EXCEPTIONS[key] = event_message rescue StandardError => e debug('Unable to report exception to telemetry', e) end def create_message stack_frame_function, stack_frame_type, message_exception_type, data, exception, message message_for_exception = if exception exception.cs__respond_to?(:message) ? exception.message : exception else message end module_name = exception ? exception.cs__class.to_s.split('::').first : nil stack_frame = Contrast::Agent::Telemetry::TelemetryException::StackFrame.build stack_frame_function, stack_frame_type, module_name message_exception = Contrast::Agent::Telemetry::TelemetryException::MessageException.build( message_exception_type, message_for_exception, module_name, stack_frame) tags = if data data else exception.cs__is_a?(Hash) ? exception : {} end message = Contrast::Agent::Telemetry::TelemetryException::Message.build tags, [message_exception] Contrast::Agent::Telemetry::TelemetryException::Event.new message end end end end