Skip to content
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

Experimental support for Open telemetry logs #292

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/semantic_logger/appender.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ module Appender
autoload :MongoDB, "semantic_logger/appender/mongodb"
autoload :NewRelic, "semantic_logger/appender/new_relic"
autoload :NewRelicLogs, "semantic_logger/appender/new_relic_logs"
autoload :OpenTelemetry, "semantic_logger/appender/open_telemetry"
autoload :Rabbitmq, "semantic_logger/appender/rabbitmq"
autoload :Splunk, "semantic_logger/appender/splunk"
autoload :SplunkHttp, "semantic_logger/appender/splunk_http"
Expand Down
84 changes: 84 additions & 0 deletions lib/semantic_logger/appender/open_telemetry.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
begin
require "opentelemetry/logs"
rescue LoadError
raise LoadError, 'Gem opentelemetry-logs-sdk is required for logging to Open Telemetry. Please add the gem "opentelemetry-logs-sdk" to your Gemfile.'
end

# Open Telemetry Appender
#
# Writes log messages, and metrics to Open Telemetry.
#
module SemanticLogger
module Appender
class OpenTelemetry < SemanticLogger::Subscriber
attr_reader :name, :version, :logger

CAPTURE_CONTEXT = ->(log) { log.set_context(:open_telemetry, ::OpenTelemetry::Context.current) }

# Create a Open Telemetry Logger appender instance.
#
# Metric only log events are sent to the Open Telemetry Metrics API instead of the Logs API.
# I.e. A metric without a message or an exception.
# To disable this default behavior set `metrics: false`
#
# Example
# SemanticLogger.add_appender(appender: :open_telemetry)
def initialize(name: "SemanticLogger",
version: SemanticLogger::VERSION,
formatter: SemanticLogger::Formatters::OpenTelemetry.new,
metrics: true,
**args,
&block)
@name = name
@version = version
@logger = ::OpenTelemetry.logger_provider.logger(name: @name, version: @version)

# Capture the current Open Telemetry context when a log entry is captured.
# Prevents duplicate subscribers as long as it is from a constant.
SemanticLogger.on_log(CAPTURE_CONTEXT)

super(formatter: formatter, metrics: metrics, **args, &block)
end

def log(log)
# return log_metric(log) if metrics && log.metric_only?

body = formatter.call(log, self)
level = body.delete(:level)
level_index = body.delete(:level_index)
time = body.delete(:time)
payload = body.delete(:payload)

@logger.on_emit(
severity_text: level,
severity_number: level_index,
timestamp: time,
body: body.transform_keys!(&:to_s),
attributes: payload,
context: log.context[:open_telemetry] || ::OpenTelemetry::Context.current
)
true
end

# Flush all pending logs.
def flush
@logger.logger_provider.force_flush
end

# Flush pending logs and close the appender
def close
@logger.logger_provider.shutdown
end

private

# For logging metrics only log events.
# def log_metric(log)
# puts "**** TODO: Metric Only Event ****"
# ap formatter.call(log, self)
# ap log.payload
# true
# end
end
end
end
25 changes: 13 additions & 12 deletions lib/semantic_logger/formatters.rb
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
module SemanticLogger
module Formatters
autoload :Base, "semantic_logger/formatters/base"
autoload :Color, "semantic_logger/formatters/color"
autoload :Default, "semantic_logger/formatters/default"
autoload :Json, "semantic_logger/formatters/json"
autoload :Raw, "semantic_logger/formatters/raw"
autoload :OneLine, "semantic_logger/formatters/one_line"
autoload :Signalfx, "semantic_logger/formatters/signalfx"
autoload :Syslog, "semantic_logger/formatters/syslog"
autoload :Fluentd, "semantic_logger/formatters/fluentd"
autoload :Logfmt, "semantic_logger/formatters/logfmt"
autoload :SyslogCee, "semantic_logger/formatters/syslog_cee"
autoload :NewRelicLogs, "semantic_logger/formatters/new_relic_logs"
autoload :Base, "semantic_logger/formatters/base"
autoload :Color, "semantic_logger/formatters/color"
autoload :Default, "semantic_logger/formatters/default"
autoload :Json, "semantic_logger/formatters/json"
autoload :Raw, "semantic_logger/formatters/raw"
autoload :OneLine, "semantic_logger/formatters/one_line"
autoload :OpenTelemetry, "semantic_logger/formatters/open_telemetry"
autoload :Signalfx, "semantic_logger/formatters/signalfx"
autoload :Syslog, "semantic_logger/formatters/syslog"
autoload :Fluentd, "semantic_logger/formatters/fluentd"
autoload :Logfmt, "semantic_logger/formatters/logfmt"
autoload :SyslogCee, "semantic_logger/formatters/syslog_cee"
autoload :NewRelicLogs, "semantic_logger/formatters/new_relic_logs"

# Return formatter that responds to call.
#
Expand Down
40 changes: 40 additions & 0 deletions lib/semantic_logger/formatters/open_telemetry.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
require "json"
module SemanticLogger
module Formatters
class OpenTelemetry < Raw
# Log level
def level
hash[:level] = log.level.to_s
hash[:level_index] = severity_number(log.level_index)
end

# Payload is submitted directly as attributes
def payload
return unless log.payload.respond_to?(:empty?) && !log.payload.empty?

hash[:payload] = log.payload.transform_keys!(&:to_s)
end

private

def severity_number(severity)
case severity
when :trace
::OpenTelemetry::Logs::SeverityNumber::SEVERITY_NUMBER_TRACE
when :debug
::OpenTelemetry::Logs::SeverityNumber::SEVERITY_NUMBER_DEBUG
when :info
::OpenTelemetry::Logs::SeverityNumber::SEVERITY_NUMBER_INFO
when :warn
::OpenTelemetry::Logs::SeverityNumber::SEVERITY_NUMBER_WARN
when :error
::OpenTelemetry::Logs::SeverityNumber::SEVERITY_NUMBER_ERROR
when :fatal
::OpenTelemetry::Logs::SeverityNumber::SEVERITY_NUMBER_FATAL
else
::OpenTelemetry::Logs::SeverityNumber::SEVERITY_NUMBER_UNSPECIFIED
end
end
end
end
end
Loading