-
Notifications
You must be signed in to change notification settings - Fork 3.5k
Allow Logstash to write its logs in JSON format #4820
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
# encoding: utf-8 | ||
require "logstash/namespace" | ||
require "logstash/logging" | ||
require "logstash/json" | ||
|
||
module LogStash; class Logging; class JSON | ||
def initialize(io) | ||
raise ArgumentError, "Expected IO, got #{io.class.name}" unless io.is_a?(IO) | ||
|
||
@io = io | ||
@lock = Mutex.new | ||
end | ||
|
||
def <<(obj) | ||
serialized = LogStash::Json.dump(obj) | ||
@lock.synchronize do | ||
@io.puts(serialized) | ||
@io.flush | ||
end | ||
end | ||
end; end; end |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -102,6 +102,10 @@ class LogStash::Runner < Clamp::Command | |
I18n.t("logstash.runner.flag.allow-env"), | ||
:attribute_name => :allow_env, :default => false | ||
|
||
option ["--[no-]log-in-json"], :flag, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. to be consistent, this pr should add the flag according to the current state of master. right now it should use "-", but if it happens to be merged after logstash.yml, then the pr needs to be changed |
||
I18n.t("logstash.runner.flag.log-in-json"), | ||
:default => false | ||
|
||
def pipeline_workers=(pipeline_workers_value) | ||
@pipeline_settings[:pipeline_workers] = validate_positive_integer(pipeline_workers_value) | ||
end | ||
|
@@ -136,7 +140,7 @@ def execute | |
require "logstash/util/java_version" | ||
require "stud/task" | ||
require "cabin" # gem 'cabin' | ||
|
||
require "logstash/logging/json" | ||
|
||
# Configure Logstash logging facility, this need to be done before everything else to | ||
# make sure the logger has the correct settings and the log level is correctly defined. | ||
|
@@ -326,11 +330,20 @@ def configure_logging(path) | |
:path => path, :error => e)) | ||
end | ||
|
||
@logger.subscribe(STDOUT, :level => :fatal) | ||
@logger.subscribe(@log_fd) | ||
if log_in_json? | ||
@logger.subscribe(LogStash::Logging::JSON.new(STDOUT), :level => :fatal) | ||
@logger.subscribe(LogStash::Logging::JSON.new(@log_fd)) | ||
else | ||
@logger.subscribe(STDOUT, :level => :fatal) | ||
@logger.subscribe(@log_fd) | ||
end | ||
@logger.terminal "Sending logstash logs to #{path}." | ||
else | ||
@logger.subscribe(STDOUT) | ||
if log_in_json? | ||
@logger.subscribe(LogStash::Logging::JSON.new(STDOUT)) | ||
else | ||
@logger.subscribe(STDOUT) | ||
end | ||
end | ||
|
||
if debug_config? && @logger.level != :debug | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,12 +8,12 @@ | |
|
||
describe LogStash::Plugin do | ||
it "should fail lookup on inexisting type" do | ||
expect_any_instance_of(Cabin::Channel).to receive(:debug).once | ||
#expect_any_instance_of(Cabin::Channel).to receive(:debug).once | ||
expect { LogStash::Plugin.lookup("badbadtype", "badname") }.to raise_error(LogStash::PluginLoadingError) | ||
end | ||
|
||
it "should fail lookup on inexisting name" do | ||
expect_any_instance_of(Cabin::Channel).to receive(:debug).once | ||
#expect_any_instance_of(Cabin::Channel).to receive(:debug).once | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. +1 for removing them. |
||
expect { LogStash::Plugin.lookup("filter", "badname") }.to raise_error(LogStash::PluginLoadingError) | ||
end | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,7 +3,10 @@ | |
require "logstash/runner" | ||
require "stud/task" | ||
require "stud/trap" | ||
require "stud/temporary" | ||
require "logstash/util/java_version" | ||
require "logstash/logging/json" | ||
require "json" | ||
|
||
class NullRunner | ||
def run(args); end | ||
|
@@ -16,7 +19,7 @@ def run(args); end | |
|
||
before :each do | ||
allow(Cabin::Channel).to receive(:get).with(LogStash).and_return(channel) | ||
allow(channel).to receive(:subscribe).with(any_args) | ||
allow(channel).to receive(:subscribe).with(any_args).and_call_original | ||
end | ||
|
||
describe "argument parsing" do | ||
|
@@ -95,6 +98,32 @@ def run(args); end | |
end | ||
end | ||
|
||
context "--log-in-json" do | ||
subject { LogStash::Runner.new("") } | ||
let(:logfile) { Stud::Temporary.file } | ||
let(:args) { [ "--log-in-json", "-l", logfile.path, "-e", "input {} output{}" ] } | ||
|
||
after do | ||
logfile.close | ||
File.unlink(logfile.path) | ||
end | ||
|
||
before do | ||
expect(channel).to receive(:subscribe).with(kind_of(LogStash::Logging::JSON)).and_call_original | ||
subject.run(args) | ||
|
||
# Log file should have stuff in it. | ||
expect(logfile.stat.size).to be > 0 | ||
end | ||
|
||
it "should log in valid json. One object per line." do | ||
logfile.each_line do |line| | ||
expect(line).not_to be_empty | ||
expect { JSON.parse(line) }.not_to raise_error | ||
end | ||
end | ||
end | ||
|
||
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We may need to add test for the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Adding Can you help me understand what testing --debug would help with? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jordansissel Correct, you are right it feel more like an integration test. but this error was found when I ran logstash with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've updated (see spec_helper) the tests to always force debug level + json logging (at least, as far as I can tell) so that we can catch some problems if they come up. |
||
describe "--config-test" do | ||
subject { LogStash::Runner.new("") } | ||
let(:args) { ["-t", "-e", pipeline_string] } | ||
|
@@ -150,14 +179,14 @@ def run(args); end | |
it "should set 'debug_config' to false by default" do | ||
expect(LogStash::Config::Loader).to receive(:new).with(anything, false).and_call_original | ||
expect(LogStash::Pipeline).to receive(:new).with(pipeline_string, hash_including(:debug_config => false)).and_return(pipeline) | ||
args = ["--debug", "-e", pipeline_string] | ||
args = ["--debug", "-e", pipeline_string, "-l", "/dev/null", "--log-in-json"] | ||
subject.run("bin/logstash", args) | ||
end | ||
|
||
it "should allow overriding debug_config" do | ||
expect(LogStash::Config::Loader).to receive(:new).with(anything, true).and_call_original | ||
expect(LogStash::Pipeline).to receive(:new).with(pipeline_string, hash_including(:debug_config => true)).and_return(pipeline) | ||
args = ["--debug", "--debug-config", "-e", pipeline_string] | ||
args = ["--debug", "--debug-config", "-e", pipeline_string, "-l", "/dev/null", "--log-in-json"] | ||
subject.run("bin/logstash", args) | ||
end | ||
end | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,6 +6,34 @@ | |
|
||
require "logstash/devutils/rspec/spec_helper" | ||
|
||
class JSONIOThingy < IO | ||
def initialize; end | ||
def flush; end | ||
|
||
def puts(payload) | ||
# Ensure that all log payloads are valid json. | ||
LogStash::Json.load(payload) | ||
end | ||
end | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh neat idea. |
||
|
||
RSpec.configure do |c| | ||
c.before do | ||
# Force Cabin to always have a JSON subscriber. The main purpose of this | ||
# is to catch crashes in json serialization for our logs. JSONIOThingy | ||
# exists to validate taht what LogStash::Logging::JSON emits is always | ||
# valid JSON. | ||
jsonvalidator = JSONIOThingy.new | ||
allow(Cabin::Channel).to receive(:new).and_wrap_original do |m, *args| | ||
logger = m.call(*args) | ||
logger.level = :debug | ||
logger.subscribe(LogStash::Logging::JSON.new(jsonvalidator)) | ||
|
||
logger | ||
end | ||
end | ||
|
||
end | ||
|
||
def installed_plugins | ||
Gem::Specification.find_all.select { |spec| spec.metadata["logstash_plugin"] }.map { |plugin| plugin.name } | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might be good to add the
# encoding: utf-8
header to appease the utf gods.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done