Skip to content

Commit

Permalink
feat: Create Logs SDK LoggerProvider
Browse files Browse the repository at this point in the history
* Add OpenTelemetry::SDK::Logs::Export constants
* Create OpenTelemetry::SDK::Logs::LoggerProvider
* Implement LoggerProvider#logger
* Implement LoggerProvider#shutdown
* Implement LoggerProvider#force_flush
* Create no-op LogRecordProcessor
  • Loading branch information
kaylareopelle committed Oct 9, 2023
1 parent afc7bc5 commit de7f36c
Show file tree
Hide file tree
Showing 11 changed files with 451 additions and 26 deletions.
5 changes: 5 additions & 0 deletions logs_sdk/Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,8 @@
source 'https://rubygems.org'

gemspec

gem 'opentelemetry-api', path: '../api'
gem 'opentelemetry-logs-api', path: '../logs_api'
gem 'opentelemetry-sdk', path: '../sdk'
gem 'opentelemetry-test-helpers', path: '../test_helpers'
3 changes: 2 additions & 1 deletion logs_sdk/lib/opentelemetry-logs-sdk.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@
#
# SPDX-License-Identifier: Apache-2.0

require 'opentelemetry'
require 'opentelemetry/sdk'
require 'opentelemetry/sdk/logs'
require 'opentelemetry/sdk/logs/version'
4 changes: 4 additions & 0 deletions logs_sdk/lib/opentelemetry/sdk/logs.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
# SPDX-License-Identifier: Apache-2.0

require_relative 'logs/version'
require_relative 'logs/logger'
require_relative 'logs/logger_provider'
require_relative 'logs/log_record_processor'
require_relative 'logs/export'

module OpenTelemetry
module SDK
Expand Down
24 changes: 24 additions & 0 deletions logs_sdk/lib/opentelemetry/sdk/logs/export.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# frozen_string_literal: true

# Copyright The OpenTelemetry Authors
#
# SPDX-License-Identifier: Apache-2.0

module OpenTelemetry
module SDK
module Logs
# The export module contains result codes for LoggerProvider#force_flush
# and LoggerProvider#shutdown
module Export
# The operation finished successfully.
SUCCESS = 0

# The operation finished with an error.
FAILURE = 1

# The operation timed out.
TIMEOUT = 2
end
end
end
end
47 changes: 47 additions & 0 deletions logs_sdk/lib/opentelemetry/sdk/logs/log_record_processor.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# frozen_string_literal: true

# Copyright The OpenTelemetry Authors
#
# SPDX-License-Identifier: Apache-2.0

module OpenTelemetry
module SDK
module Logs
# LogRecordProcessor describes a duck type and provides synchronous no-op hooks for when a
# {LogRecord} is started or when a {LogRecord} is ended. It is not required to subclass this
# class to provide an implementation of LogRecordProcessor, provided the interface is
# satisfied.
class LogRecordProcessor
# Called when a {LogRecord} is emitted. Subsequent calls are not
# permitted after shutdown is called.
# @param [LogRecord] log_record The emitted {LogRecord}
# @param [Context] context The resolved Context
def on_emit(log_record, context); end

# Export all log records to the configured `Exporter` that have not yet
# been exported.
#
# This method should only be called in cases where it is absolutely
# necessary, such as when using some FaaS providers that may suspend
# the process after an invocation, but before the `Processor` exports
# the completed spans.
#
# @param [optional Numeric] timeout An optional timeout in seconds.
# @return [Integer] Export::SUCCESS if no error occurred, Export::FAILURE if
# a non-specific failure occurred, Export::TIMEOUT if a timeout occurred.
def force_flush(timeout: nil)
Export::SUCCESS
end

# Called when {LoggerProvider#shutdown} is called.
#
# @param [optional Numeric] timeout An optional timeout in seconds.
# @return [Integer] Export::SUCCESS if no error occurred, Export::FAILURE if
# a non-specific failure occurred, Export::TIMEOUT if a timeout occurred.
def shutdown(timeout: nil)
Export::SUCCESS
end
end
end
end
end
33 changes: 33 additions & 0 deletions logs_sdk/lib/opentelemetry/sdk/logs/logger.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# frozen_string_literal: true

# Copyright The OpenTelemetry Authors
#
# SPDX-License-Identifier: Apache-2.0

module OpenTelemetry
module SDK
module Logs
# The SDK implementation of OpenTelemetry::Logs::Logger
class Logger < OpenTelemetry::Logs::Logger
attr_reader :instrumentation_scope, :logger_provider

# @api private
#
# Returns a new {OpenTelemetry::SDK::Logs::Logger} instance. This should
# not be called directly. New loggers should be created using
# {LoggerProvider#logger}.
#
# @param [String] name Instrumentation package name
# @param [String] version Instrumentation package version
# @param [LoggerProvider] logger_provider The {LoggerProvider} that
# initialized the logger
#
# @return [OpenTelemetry::SDK::Logs::Logger]
def initialize(name, version, logger_provider)
@instrumentation_scope = InstrumentationScope.new(name, version)
@logger_provider = logger_provider
end
end
end
end
end
127 changes: 127 additions & 0 deletions logs_sdk/lib/opentelemetry/sdk/logs/logger_provider.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
# frozen_string_literal: true

# Copyright The OpenTelemetry Authors
#
# SPDX-License-Identifier: Apache-2.0

module OpenTelemetry
module SDK
module Logs
# The SDK implementation of OpenTelemetry::Logs::LoggerProvider.
class LoggerProvider < OpenTelemetry::Logs::LoggerProvider
attr_reader :resource, :log_record_processors

UNEXPECTED_ERROR_MESSAGE = 'unexpected error in ' \
'OpenTelemetry::SDK::Logs::LoggerProvider#%s'
# Returns a new LoggerProvider instance.
#
# @param [optional Resource] resource The resource to associate with
# new LogRecords created by {Logger}s created by this LoggerProvider.
# @param [optional Array] log_record_processors The
# {LogRecordProcessor}s to associate with this LoggerProvider.
#
# @return [OpenTelemetry::SDK::Logs::LoggerProvider]
def initialize(
resource: OpenTelemetry::SDK::Resources::Resource.create,
log_record_processors: []
)
@log_record_processors = log_record_processors
@mutex = Mutex.new
@resource = resource
@stopped = false
end

# Creates an {OpenTelemetry::SDK::Logs::Logger} instance.
#
# @param [optional String] name Instrumentation package name
# @param [optional String] version Instrumentation package version
#
# @return [OpenTelemetry::SDK::Logs::Logger]
def logger(name = nil, version = nil)
name ||= ''
version ||= ''

OpenTelemetry.logger.warn('LoggerProvider#logger called without providing a logger name.') if name.empty?

OpenTelemetry::SDK::Logs::Logger.new(name, version, self)
end

# Adds a new log record processor to this LoggerProvider's
# log_record_processors.
#
# @param [LogRecordProcessor] log_record_processor The
# {LogRecordProcessor} to add to this LoggerProvider.
def add_log_record_processor(log_record_processor)
@mutex.synchronize do
@log_record_processors = log_record_processors.dup.push(log_record_processor)
end
end

# Attempts to stop all the activity for this LoggerProvider. Calls
# {LogRecordProcessor#shutdown} for all registered {LogRecordProcessor}s.
#
# This operation may block until all log records are processed. Must
# be called before turning off the main application to ensure all data
# are processed and exported.
#
# After this is called all newly created {LogRecord}s will be no-op.
#
# @param [optional Numeric] timeout An optional timeout in seconds.
# @return [Integer] Export::SUCCESS if no error occurred, Export::FAILURE if
# a non-specific failure occurred, Export::TIMEOUT if a timeout occurred.
def shutdown(timeout: nil)
@mutex.synchronize do
if @stopped
OpenTelemetry.logger.warn('LoggerProvider#shutdown called multiple times.')
return OpenTelemetry::SDK::Logs::Export::FAILURE
end

start_time = OpenTelemetry::Common::Utilities.timeout_timestamp
results = log_record_processors.map do |processor|
remaining_timeout = OpenTelemetry::Common::Utilities.maybe_timeout(timeout, start_time)
break [OpenTelemetry::SDK::Logs::Export::TIMEOUT] if remaining_timeout&.zero?

processor.shutdown(timeout: remaining_timeout)
end

@stopped = true
results.max || OpenTelemetry::SDK::Logs::Export::SUCCESS
end
rescue StandardError => e
OpenTelemetry.handle_error(exception: e, message: UNEXPECTED_ERROR_MESSAGE % __method__)
Export::FAILURE
end

# Immediately export all {LogRecord}s that have not yet been exported
# for all the registered {LogRecordProcessor}s.
#
# This method should only be called in cases where it is absolutely
# necessary, such as when using some FaaS providers that may suspend
# the process after an invocation, but before the {LogRecordProcessor}
# exports the completed {LogRecord}s.
#
# @param [optional Numeric] timeout An optional timeout in seconds.
# @return [Integer] Export::SUCCESS if no error occurred, Export::FAILURE if
# a non-specific failure occurred, Export::TIMEOUT if a timeout occurred.
def force_flush(timeout: nil)
@mutex.synchronize do
return Export::SUCCESS if @stopped

start_time = OpenTelemetry::Common::Utilities.timeout_timestamp
results = log_record_processors.map do |processor|
remaining_timeout = OpenTelemetry::Common::Utilities.maybe_timeout(timeout, start_time)
return Export::TIMEOUT if remaining_timeout&.zero?

processor.force_flush(timeout: remaining_timeout)
end

results.max || Export::SUCCESS
end
rescue StandardError => e
OpenTelemetry.handle_error(exception: e, message: UNEXPECTED_ERROR_MESSAGE % __method__)
Export::FAILURE
end
end
end
end
end
15 changes: 9 additions & 6 deletions logs_sdk/opentelemetry-logs-sdk.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,18 @@ Gem::Specification.new do |spec|
spec.require_paths = ['lib']
spec.required_ruby_version = '>= 3.0'

spec.add_dependency 'opentelemetry-logs-api', '~> 0.1.0'
spec.add_dependency 'opentelemetry-api', '~> 1.2'
spec.add_dependency 'opentelemetry-logs-api', '~> 0.1'
spec.add_dependency 'opentelemetry-sdk', '~> 1.3'

spec.add_development_dependency 'bundler', '>= 1.17'
spec.add_development_dependency 'minitest', '~> 5.0'
spec.add_development_dependency 'rake', '~> 12.0'
spec.add_development_dependency 'rubocop', '~> 1.51.0'
spec.add_development_dependency 'simplecov', '~> 0.17'
spec.add_development_dependency 'minitest', '~> 5.19'
spec.add_development_dependency 'opentelemetry-test-helpers', '~> 0.4'
spec.add_development_dependency 'rake', '~> 13.0'
spec.add_development_dependency 'rubocop', '~> 1.56'
spec.add_development_dependency 'simplecov', '~> 0.22'
spec.add_development_dependency 'yard', '~> 0.9'
spec.add_development_dependency 'yard-doctest', '~> 0.1.6'
spec.add_development_dependency 'yard-doctest', '~> 0.1.17'

if spec.respond_to?(:metadata)
spec.metadata['changelog_uri'] = "https://open-telemetry.github.io/opentelemetry-ruby/opentelemetry-logs-sdk/v#{OpenTelemetry::SDK::Logs::VERSION}/file.CHANGELOG.html"
Expand Down
22 changes: 3 additions & 19 deletions logs_sdk/test/.rubocop.yml
Original file line number Diff line number Diff line change
@@ -1,24 +1,8 @@
# inherit_from: .rubocop_todo.yml
inherit_from: ../.rubocop.yml

AllCops:
TargetRubyVersion: '3.0'

Lint/UnusedMethodArgument:
Enabled: false
Metrics/AbcSize:
Metrics/BlockLength:
Enabled: false
Metrics/LineLength:
Enabled: false
Metrics/MethodLength:
Max: 50
Metrics/PerceivedComplexity:
Max: 30
Metrics/CyclomaticComplexity:
Max: 20
Metrics/ParameterLists:
Enabled: false
Naming/FileName:
Exclude:
- 'lib/opentelemetry-logs-sdk.rb'
Style/ModuleFunction:
Metrics/AbcSize:
Enabled: false
Loading

0 comments on commit de7f36c

Please sign in to comment.