Loading lib/labkit/logging.rb +1 −0 Original line number Diff line number Diff line Loading @@ -6,5 +6,6 @@ module Labkit module Logging autoload :GRPC, "labkit/logging/grpc" autoload :Sanitizer, "labkit/logging/sanitizer" autoload :JsonLogger, "labkit/logging/json_logger" end end lib/labkit/logging/json_logger.rb 0 → 100644 +44 −0 Original line number Diff line number Diff line # frozen_string_literal: true require "time" require "logger" require "json" module Labkit module Logging class JsonLogger < ::Logger def self.log_level(fallback: ::Logger::DEBUG) ENV.fetch("GITLAB_LOG_LEVEL", fallback) end def initialize(path, level: JsonLogger.log_level) super end def format_message(severity, timestamp, progname, message) data = default_attributes data[:severity] = severity data[:time] = timestamp.utc.iso8601(3) data[Labkit::Correlation::CorrelationId::LOG_KEY] = Labkit::Correlation::CorrelationId.current_id case message when String data[:message] = message when Hash data.merge!(message) end dump_json(data) << "\n" end private def default_attributes {} end def dump_json(data) JSON.generate(data) end end end end spec/labkit/logging/json_logger_spec.rb 0 → 100644 +123 −0 Original line number Diff line number Diff line # frozen_string_literal: true RSpec.describe Labkit::Logging::JsonLogger do include StubENV subject { described_class.new("/dev/null") } let(:now) { Time.now } before do allow(Labkit::Correlation::CorrelationId).to receive(:current_id).and_return("new-correlation-id") end describe ".initialize" do it "builds logger using log_level" do expect(described_class).to receive(:log_level).and_return(:warn) expect(subject.level).to eq(described_class::WARN) end it "raises ArgumentError if invalid log level" do allow(described_class).to receive(:log_level).and_return(:invalid) expect { subject.level }.to raise_error(ArgumentError, "invalid log level: invalid") end using RSpec::Parameterized::TableSyntax where(:env_value, :resulting_level) do 0 | described_class::DEBUG :debug | described_class::DEBUG "debug" | described_class::DEBUG "DEBUG" | described_class::DEBUG "DeBuG" | described_class::DEBUG 1 | described_class::INFO :info | described_class::INFO "info" | described_class::INFO "INFO" | described_class::INFO "InFo" | described_class::INFO 2 | described_class::WARN :warn | described_class::WARN "warn" | described_class::WARN "WARN" | described_class::WARN "WaRn" | described_class::WARN 3 | described_class::ERROR :error | described_class::ERROR "error" | described_class::ERROR "ERROR" | described_class::ERROR "ErRoR" | described_class::ERROR 4 | described_class::FATAL :fatal | described_class::FATAL "fatal" | described_class::FATAL "FATAL" | described_class::FATAL "FaTaL" | described_class::FATAL 5 | described_class::UNKNOWN :unknown | described_class::UNKNOWN "unknown" | described_class::UNKNOWN "UNKNOWN" | described_class::UNKNOWN "UnKnOwN" | described_class::UNKNOWN end with_them do it "builds logger if valid log level" do stub_env("GITLAB_LOG_LEVEL", env_value) expect(subject.level).to eq(resulting_level) end end end describe ".log_level" do context "if GITLAB_LOG_LEVEL is set" do before do stub_env("GITLAB_LOG_LEVEL", described_class::ERROR) end it "returns value of GITLAB_LOG_LEVEL" do expect(described_class.log_level).to eq(described_class::ERROR) end it "ignores fallback" do expect(described_class.log_level(fallback: described_class::FATAL)).to eq(described_class::ERROR) end end context "if GITLAB_LOG_LEVEL is not set" do it "returns default fallback DEBUG" do expect(described_class.log_level).to eq(described_class::DEBUG) end it "returns passed fallback" do expect(described_class.log_level(fallback: described_class::FATAL)).to eq(described_class::FATAL) end end end it "appends newline" do output = subject.format_message("INFO", now, "test", "Hello world") expect(output).to end_with("\n") end it "formats strings" do output = subject.format_message("INFO", now, "test", "Hello world") data = JSON.parse(output) expect(data["severity"]).to eq("INFO") expect(data["time"]).to eq(now.utc.iso8601(3)) expect(data["message"]).to eq("Hello world") expect(data["correlation_id"]).to eq("new-correlation-id") end it "formats hashes" do output = subject.format_message("INFO", now, "test", { hello: 1 }) data = JSON.parse(output) expect(data["severity"]).to eq("INFO") expect(data["time"]).to eq(now.utc.iso8601(3)) expect(data["hello"]).to eq(1) expect(data["message"]).to be_nil expect(data["correlation_id"]).to eq("new-correlation-id") end end Loading
lib/labkit/logging.rb +1 −0 Original line number Diff line number Diff line Loading @@ -6,5 +6,6 @@ module Labkit module Logging autoload :GRPC, "labkit/logging/grpc" autoload :Sanitizer, "labkit/logging/sanitizer" autoload :JsonLogger, "labkit/logging/json_logger" end end
lib/labkit/logging/json_logger.rb 0 → 100644 +44 −0 Original line number Diff line number Diff line # frozen_string_literal: true require "time" require "logger" require "json" module Labkit module Logging class JsonLogger < ::Logger def self.log_level(fallback: ::Logger::DEBUG) ENV.fetch("GITLAB_LOG_LEVEL", fallback) end def initialize(path, level: JsonLogger.log_level) super end def format_message(severity, timestamp, progname, message) data = default_attributes data[:severity] = severity data[:time] = timestamp.utc.iso8601(3) data[Labkit::Correlation::CorrelationId::LOG_KEY] = Labkit::Correlation::CorrelationId.current_id case message when String data[:message] = message when Hash data.merge!(message) end dump_json(data) << "\n" end private def default_attributes {} end def dump_json(data) JSON.generate(data) end end end end
spec/labkit/logging/json_logger_spec.rb 0 → 100644 +123 −0 Original line number Diff line number Diff line # frozen_string_literal: true RSpec.describe Labkit::Logging::JsonLogger do include StubENV subject { described_class.new("/dev/null") } let(:now) { Time.now } before do allow(Labkit::Correlation::CorrelationId).to receive(:current_id).and_return("new-correlation-id") end describe ".initialize" do it "builds logger using log_level" do expect(described_class).to receive(:log_level).and_return(:warn) expect(subject.level).to eq(described_class::WARN) end it "raises ArgumentError if invalid log level" do allow(described_class).to receive(:log_level).and_return(:invalid) expect { subject.level }.to raise_error(ArgumentError, "invalid log level: invalid") end using RSpec::Parameterized::TableSyntax where(:env_value, :resulting_level) do 0 | described_class::DEBUG :debug | described_class::DEBUG "debug" | described_class::DEBUG "DEBUG" | described_class::DEBUG "DeBuG" | described_class::DEBUG 1 | described_class::INFO :info | described_class::INFO "info" | described_class::INFO "INFO" | described_class::INFO "InFo" | described_class::INFO 2 | described_class::WARN :warn | described_class::WARN "warn" | described_class::WARN "WARN" | described_class::WARN "WaRn" | described_class::WARN 3 | described_class::ERROR :error | described_class::ERROR "error" | described_class::ERROR "ERROR" | described_class::ERROR "ErRoR" | described_class::ERROR 4 | described_class::FATAL :fatal | described_class::FATAL "fatal" | described_class::FATAL "FATAL" | described_class::FATAL "FaTaL" | described_class::FATAL 5 | described_class::UNKNOWN :unknown | described_class::UNKNOWN "unknown" | described_class::UNKNOWN "UNKNOWN" | described_class::UNKNOWN "UnKnOwN" | described_class::UNKNOWN end with_them do it "builds logger if valid log level" do stub_env("GITLAB_LOG_LEVEL", env_value) expect(subject.level).to eq(resulting_level) end end end describe ".log_level" do context "if GITLAB_LOG_LEVEL is set" do before do stub_env("GITLAB_LOG_LEVEL", described_class::ERROR) end it "returns value of GITLAB_LOG_LEVEL" do expect(described_class.log_level).to eq(described_class::ERROR) end it "ignores fallback" do expect(described_class.log_level(fallback: described_class::FATAL)).to eq(described_class::ERROR) end end context "if GITLAB_LOG_LEVEL is not set" do it "returns default fallback DEBUG" do expect(described_class.log_level).to eq(described_class::DEBUG) end it "returns passed fallback" do expect(described_class.log_level(fallback: described_class::FATAL)).to eq(described_class::FATAL) end end end it "appends newline" do output = subject.format_message("INFO", now, "test", "Hello world") expect(output).to end_with("\n") end it "formats strings" do output = subject.format_message("INFO", now, "test", "Hello world") data = JSON.parse(output) expect(data["severity"]).to eq("INFO") expect(data["time"]).to eq(now.utc.iso8601(3)) expect(data["message"]).to eq("Hello world") expect(data["correlation_id"]).to eq("new-correlation-id") end it "formats hashes" do output = subject.format_message("INFO", now, "test", { hello: 1 }) data = JSON.parse(output) expect(data["severity"]).to eq("INFO") expect(data["time"]).to eq(now.utc.iso8601(3)) expect(data["hello"]).to eq(1) expect(data["message"]).to be_nil expect(data["correlation_id"]).to eq("new-correlation-id") end end