Skip to content

Commit

Permalink
Experimental support for Open telemetry logs
Browse files Browse the repository at this point in the history
  • Loading branch information
reidmorrison committed Jul 23, 2024
1 parent 364f3b9 commit ca10594
Show file tree
Hide file tree
Showing 4 changed files with 132 additions and 12 deletions.
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
100 changes: 100 additions & 0 deletions lib/semantic_logger/appender/open_telemetry.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
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?

ap formatter.call(log, self)
ap log.payload

@logger.on_emit(
severity_text: log.level.to_s,
severity_number: severity_number(log.level),
timestamp: log.time,
body: formatter.call(log, self),
attributes: log.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

def severity_number(severity)
case severity.downcase
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
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
18 changes: 18 additions & 0 deletions lib/semantic_logger/formatters/open_telemetry.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
require "json"
module SemanticLogger
module Formatters
class OpenTelemetry < Raw
# Remove the following fields since they are sent via the OpenTelemetry API
def time
end

# Log level
def level
end

# Payload is submitted directly as attributes
def payload
end
end
end
end

0 comments on commit ca10594

Please sign in to comment.