# encoding: utf-8
# This file is distributed under New Relic's license terms.
# See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details.

require 'logger'
require 'fileutils'
require 'new_relic/agent/hostname'

module NewRelic
  module Agent
    class AuditLogger
      def initialize
        @enabled = NewRelic::Agent.config[:'audit_log.enabled']
        @endpoints = NewRelic::Agent.config[:'audit_log.endpoints']
        @encoder = NewRelic::Agent::NewRelicService::Encoders::Identity
      end

      attr_writer :enabled

      def enabled?
        @enabled
      end

      def setup?
        !@log.nil?
      end

      def log_request(uri, data, marshaller)
        return unless enabled? && allowed_endpoint?(uri)

        setup_logger unless setup?
        request_body = if marshaller.class.human_readable?
          marshaller.dump(data, :encoder => @encoder)
        else
          marshaller.prepare(data, :encoder => @encoder).inspect
        end
        @log.info("REQUEST: #{uri}")
        @log.info("REQUEST BODY: #{request_body}")
      rescue StandardError, SystemStackError, SystemCallError => e
        ::NewRelic::Agent.logger.warn("Failed writing to audit log", e)
      rescue Exception => e
        ::NewRelic::Agent.logger.warn("Failed writing to audit log with exception. Re-raising in case of interupt.", e)
        raise
      end

      def allowed_endpoint?(uri)
        @endpoints.any? { |endpoint| uri =~ endpoint }
      end

      def setup_logger
        if wants_stdout?
          # Using $stdout global for easier reassignment in testing
          @log = ::Logger.new($stdout)
          ::NewRelic::Agent.logger.info("Audit log enabled to STDOUT")
        elsif path = ensure_log_path
          @log = ::Logger.new(path)
          ::NewRelic::Agent.logger.info("Audit log enabled at '#{path}'")
        else
          @log = NewRelic::Agent::NullLogger.new
        end

        @log.formatter = create_log_formatter
      end

      def ensure_log_path
        path = File.expand_path(NewRelic::Agent.config[:'audit_log.path'])
        log_dir = File.dirname(path)

        begin
          FileUtils.mkdir_p(log_dir)
          FileUtils.touch(path)
        rescue SystemCallError => e
          msg = "Audit log disabled, failed opening log at '#{path}': #{e}"
          ::NewRelic::Agent.logger.warn(msg)
          path = nil
        end

        path
      end

      def wants_stdout?
        ::NewRelic::Agent.config[:'audit_log.path'].upcase == "STDOUT"
      end

      def create_log_formatter
        @hostname = NewRelic::Agent::Hostname.get
        @prefix = wants_stdout? ? '** [NewRelic]' : ''
        Proc.new do |severity, time, progname, msg|
          "#{@prefix}[#{time} #{@hostname} (#{$$})] : #{msg}\n"
        end
      end
    end
  end
end
