Skip to content

Instantly share code, notes, and snippets.

@nghinv
Forked from adrienmo/logstash-output-sentry.md
Created June 20, 2021 09:21
Show Gist options
  • Save nghinv/d9898ffc5b128b8ea18096ee8011e59b to your computer and use it in GitHub Desktop.
Save nghinv/d9898ffc5b128b8ea18096ee8011e59b to your computer and use it in GitHub Desktop.
Logstash output to Sentry plugin + logstash configuration example

To install the plugin you can create a folder for logstash plugins, e.g. :

/etc/logstash/plugins/

Be sure you respect logstash requirements about the plugin path "$PATH/logstash/TYPE/NAME.rb", e.g. :

/etc/logstash/plugins/logstash/outputs/sentry.rb

Then start logstash manually with the options :

bin/logstash -p /etc/logstash/plugins/

Or modify the logstash service configuration /etc/init.d/logstash :

LS_OPTS="-p /etc/logstash/plugins/"
# The MIT License (MIT)
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
require 'logstash/outputs/base'
require 'logstash/namespace'
class LogStash::Outputs::Sentry < LogStash::Outputs::Base
config_name 'sentry'
milestone 1
config :key, :validate => :string, :required => true
config :secret, :validate => :string, :required => true
config :project_id, :validate => :string, :required => true
config :host, :validate => :string, :required => true
public
def register
require 'net/https'
require 'uri'
@url = "#{host}/api/#{project_id}/store/"
@uri = URI.parse(@url)
@client = Net::HTTP.new(@uri.host, @uri.port)
if @url.include? "https"
@client.use_ssl = true
@client.verify_mode = OpenSSL::SSL::VERIFY_NONE
end
@logger.debug("Client", :client => @client.inspect)
end
public
def receive(event)
return unless output?(event)
require 'json'
require 'securerandom'
packet = {
:event_id => SecureRandom.uuid.gsub('-', ''),
:timestamp => event['@timestamp'],
:message => event['message'],
:level => event['level'],
:server_name => event['host'],
:extra => event['extra'].to_hash
}
@logger.debug("Sentry packet", :sentry_packet => packet)
auth_header = "Sentry sentry_version=5," +
"sentry_client=raven_logstash/1.0," +
"sentry_timestamp=#{event['@timestamp'].to_i}," +
"sentry_key=#{@key}," +
"sentry_secret=#{@secret}"
request = Net::HTTP::Post.new(@uri.path)
begin
request.body = packet.to_json
request.add_field('X-Sentry-Auth', auth_header)
response = @client.request(request)
@logger.info("Sentry response", :request => request.inspect, :response => response.inspect)
raise unless response.code == '200'
rescue Exception => e
@logger.warn("Unhandled exception", :request => request.inspect, :response => response.inspect, :exception => e.inspect)
end
end
end
input {
file {
path => "/var/log/app1/access.log"
type => "app1"
}
}
filter {
grok {
match => { "message" => "%{IP:host},(?<level>(INFO|WARNING|ERROR)),%{USERNAME:filename},%{INT:line},%{USERNAME:function,%{GREEDYDATA:message}" }
overwrite => [ "message" ]
overwrite => [ "host" ]
}
mutate {
gsub => [
"level", "ERROR", "error",
"level", "WARNING", "warning",
"level", "RUN", "info"
]
rename => {
"filename" => "[extra][filename]"
"line" => "[extra][line]"
"function" => "[extra][function]"
"timestamp" => "[extra][timestamp]"
}
}
if "_grokparsefailure" in [tags] {
drop { }
}
}
output {
if [type] == "app1" {
sentry {
'host' => 'http://sentry.example.com'
'key' => 'xxxxxxxxxxxxxxxxxxxxxxxxxx'
'secret' => 'xxxxxxxxxxxxxxxxxxxxxxxxxx'
'project_id' => '1'
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment