class SemanticLogger::Appender::Http
def compress_data(data)
def compress_data(data) str = StringIO.new gz = Zlib::GzipWriter.new(str) gz << data gz.close str.string end
def default_formatter
def default_formatter SemanticLogger::Formatters::Json.new end
def delete(request_uri = path)
def delete(request_uri = path) request = Net::HTTP::Delete.new(request_uri, @header) process_request(request) end
def initialize(url:,
continue_timeout: [Float]
Default: 1.0
read_timeout: [Float]
Default: 2.0
open_timeout: [Float]
The Proc must return true or false.
Proc: Only include log messages where the supplied Proc returns true
regular expression. All other messages will be ignored.
RegExp: Only include log messages where the class name matches the supplied.
filter: [Regexp|Proc]
Default: Use the built-in formatter (See: #call)
the output from this appender
An instance of a class that implements #call, or a Proc to be used to format
formatter: [Object|Proc]
Default: SemanticLogger.default_level
Override the log level for this appender.
level: [:trace | :debug | :info | :warn | :error | :fatal]
even if the environment variables are set.
variables if they are set. If set to nil then no proxy will be used,
If this is set to :ENV, Net::HTTP will use the environment http_proxy*
Example: https://example.com/some_path
To enable SSL include https in the URL.
Example: http://user@pass:example.com/some_path
include username and password if required.
URL of proxy server to use for HTTP(s) connections. Should
proxy_url: [String]
ssl_version, verify_callback, verify_depth and verify_mode.
ca_file, ca_path, cert, cert_store, ciphers, key, ssl_timeout,
Specific SSL options: For more details see NET::HTTP.start
ssl: [Hash]
Default: false
Whether to compress the JSON string with GZip.
compress: [true|false]
Example: {"Authorization" => "Bearer BEARER_TOKEN"}
Default: {} ( do not send any custom headers)
Custom HTTP headers to send with each request.
header: [Hash]
Password for basic Authentication.
password: [String]
Default: nil ( do not use basic auth )
User name for basic Authentication.
username: [String]
Default: SemanticLogger.host
Name of this host to appear in log messages.
host: [String]
Default: SemanticLogger.application
Name of this application to appear in log messages.
application: [String]
verify_mode will default: OpenSSL::SSL::VERIFY_PEER
Example: https://example.com/some_path
To enable SSL include https in the URL.
Example: http://example.com/some_path
Valid URL to post to.
url: [String]
Parameters:
Create HTTP(S) log appender
def initialize(url:, compress: false, ssl: {}, username: nil, password: nil, header: {}, proxy_url: :ENV, open_timeout: 2.0, read_timeout: 1.0, continue_timeout: 1.0, **args, &block) @url = url @proxy_url = proxy_url @ssl_options = ssl @username = username @password = password @compress = compress @open_timeout = open_timeout @read_timeout = read_timeout @continue_timeout = continue_timeout # On Ruby v2.0 and greater, Net::HTTP.new already uses a persistent connection if the server allows it @header = { "Accept" => "application/json", "Content-Type" => "application/json", "Connection" => "keep-alive", "Keep-Alive" => "300" }.merge(header) @header["Content-Encoding"] = "gzip" if @compress uri = URI.parse(@url) @server = uri.host unless @server raise(ArgumentError, "Invalid format for :url: #{@url.inspect}. Should be similar to: 'http://hostname:port/path'") end @port = uri.port @username = uri.user if !@username && uri.user @password = uri.password if !@password && uri.password @path = uri.path # Path cannot be empty @path = "/" if @path == "" if uri.scheme == "https" @ssl_options[:use_ssl] = true @ssl_options[:verify_mode] ||= OpenSSL::SSL::VERIFY_PEER @port ||= HTTP.https_default_port else @port ||= HTTP.http_default_port end @proxy_uri = URI.parse(@proxy_url) if @proxy_url && @proxy_url != :ENV @http = nil super(**args, &block) reopen end
def log(log)
def log(log) message = formatter.call(log, self) logger.trace(message) post(message) end
def post(body, request_uri = path)
def post(body, request_uri = path) request = Net::HTTP::Post.new(request_uri, @header) process_request(request, body) end
def process_request(request, body = nil)
def process_request(request, body = nil) if body request.body = compress ? compress_data(body) : body end request.basic_auth(@username, @password) if @username response = @http.request(request) if response.is_a?(Net::HTTPSuccess) true else # Failures are logged to the global semantic logger failsafe logger (Usually stderr or file) logger.error("Bad HTTP response from: #{url} code: #{response.code}, #{response.body}") false end rescue RuntimeError => e reopen raise e end
def put(body, request_uri = path)
def put(body, request_uri = path) request = Net::HTTP::Put.new(request_uri, @header) process_request(request, body) end
def reopen
def reopen # Close open connection if any begin @http&.finish rescue IOError nil end @http = if @proxy_uri Net::HTTP.new(server, port, @proxy_uri.host, @proxy_uri.port, @proxy_uri.user, @proxy_uri.password) else Net::HTTP.new(server, port, @proxy_url) end if @ssl_options @http.methods.grep(/\A(\w+)=\z/) do |meth| key = Regexp.last_match(1).to_sym @ssl_options.key?(key) || next @http.__send__(meth, @ssl_options[key]) end end @http.open_timeout = @open_timeout @http.read_timeout = @read_timeout @http.continue_timeout = @continue_timeout @http.start end