lib/appsignal/extension/jruby.rb
# frozen_string_literal: true
require "ffi"
module Appsignal
class Extension
# JRuby extension wrapper
#
# Only loaded if the system is detected as JRuby.
#
# @api private
module Jruby # rubocop:disable Metrics/ModuleLength
extend FFI::Library
# JRuby extension String helpers.
#
# Based on the make_appsignal_string and make_ruby_string helpers from the
# AppSignal C-extension in `ext/appsignal_extension.c`.
module StringHelpers
class AppsignalString < FFI::Struct
layout :len, :size_t,
:buf, :pointer
end
def make_appsignal_string(ruby_string)
unless ruby_string.is_a?(String)
raise ArgumentError, "argument is not a string"
end
AppsignalString.new.tap do |appsignal_string|
appsignal_string[:len] = ruby_string.bytesize
appsignal_string[:buf] = FFI::MemoryPointer.from_string(ruby_string)
end
end
def make_ruby_string(appsignal_string)
appsignal_string[:buf].read_string(appsignal_string[:len]).tap do |ruby_string|
ruby_string.force_encoding(Encoding::UTF_8)
end
end
end
include StringHelpers
def self.lib_extension
if Appsignal::System.agent_platform.include?("darwin")
"dylib"
else
"so"
end
end
begin
ffi_lib File.join(File.dirname(__FILE__), "../../../ext/libappsignal.#{lib_extension}")
typedef AppsignalString.by_value, :appsignal_string
attach_function :appsignal_start, [], :void
attach_function :appsignal_stop, [], :void
attach_function :appsignal_diagnose, [], :appsignal_string
attach_function :appsignal_get_server_state,
[:appsignal_string],
:appsignal_string
attach_function :appsignal_running_in_container, [], :bool
# Metrics methods
attach_function :appsignal_set_gauge,
[:appsignal_string, :double, :pointer],
:void
attach_function :appsignal_set_host_gauge,
[:appsignal_string, :double],
:void
attach_function :appsignal_set_process_gauge,
[:appsignal_string, :double],
:void
attach_function :appsignal_increment_counter,
[:appsignal_string, :double, :pointer],
:void
attach_function :appsignal_add_distribution_value,
[:appsignal_string, :double, :pointer],
:void
# Transaction methods
attach_function :appsignal_free_transaction,
[],
:void
attach_function :appsignal_start_transaction,
[:appsignal_string, :appsignal_string, :long],
:pointer
attach_function :appsignal_start_event,
[:pointer, :long],
:void
attach_function :appsignal_finish_event,
[:pointer, :appsignal_string, :appsignal_string, :appsignal_string, :int64, :long],
:void
attach_function :appsignal_finish_event_data,
[:pointer, :appsignal_string, :appsignal_string, :pointer, :int64, :long],
:void
attach_function :appsignal_record_event,
[:pointer, :appsignal_string, :appsignal_string, :appsignal_string, :int64, :long, :long],
:void
attach_function :appsignal_record_event_data,
[:pointer, :appsignal_string, :appsignal_string, :pointer, :int64, :long, :long],
:void
attach_function :appsignal_set_transaction_error,
[:pointer, :appsignal_string, :appsignal_string, :pointer],
:void
attach_function :appsignal_set_transaction_action,
[:pointer, :appsignal_string],
:void
attach_function :appsignal_set_transaction_namespace,
[:pointer, :appsignal_string],
:void
attach_function :appsignal_set_transaction_sample_data,
[:pointer, :appsignal_string, :pointer],
:void
attach_function :appsignal_set_transaction_queue_start,
[:pointer, :long],
:void
attach_function :appsignal_set_transaction_metadata,
[:pointer, :appsignal_string, :appsignal_string],
:void
attach_function :appsignal_finish_transaction,
[:pointer, :long],
:void
attach_function :appsignal_complete_transaction,
[:pointer],
:void
attach_function :appsignal_transaction_to_json,
[:pointer],
:appsignal_string
# Data struct methods
attach_function :appsignal_free_data, [], :void
attach_function :appsignal_data_map_new, [], :pointer
attach_function :appsignal_data_array_new, [], :pointer
attach_function :appsignal_data_map_set_string,
[:pointer, :appsignal_string, :appsignal_string],
:void
attach_function :appsignal_data_map_set_integer,
[:pointer, :appsignal_string, :int64],
:void
attach_function :appsignal_data_map_set_float,
[:pointer, :appsignal_string, :double],
:void
attach_function :appsignal_data_map_set_boolean,
[:pointer, :appsignal_string, :bool],
:void
attach_function :appsignal_data_map_set_null,
[:pointer, :appsignal_string],
:void
attach_function :appsignal_data_map_set_data,
[:pointer, :appsignal_string, :pointer],
:void
attach_function :appsignal_data_array_append_string,
[:pointer, :appsignal_string],
:void
attach_function :appsignal_data_array_append_integer,
[:pointer, :int64],
:void
attach_function :appsignal_data_array_append_float,
[:pointer, :double],
:void
attach_function :appsignal_data_array_append_boolean,
[:pointer, :bool],
:void
attach_function :appsignal_data_array_append_null,
[:pointer],
:void
attach_function :appsignal_data_array_append_data,
[:pointer, :pointer],
:void
attach_function :appsignal_data_equal,
[:pointer, :pointer],
:bool
attach_function :appsignal_data_to_json,
[:pointer],
:appsignal_string
Appsignal.extension_loaded = true
rescue LoadError => err
Appsignal.logger.error(
"Failed to load extension (#{err}), please email us at " \
"support@appsignal.com"
)
Appsignal.extension_loaded = false
end
def start
appsignal_start
end
def stop
appsignal_stop
end
def diagnose
make_ruby_string(appsignal_diagnose)
end
def get_server_state(key)
state = appsignal_get_server_state(make_appsignal_string(key))
make_ruby_string state if state[:len] > 0
end
def start_transaction(transaction_id, namespace, gc_duration_ms)
transaction = appsignal_start_transaction(
make_appsignal_string(transaction_id),
make_appsignal_string(namespace),
gc_duration_ms
)
return if !transaction || transaction.null?
Transaction.new(transaction)
end
def data_map_new
Data.new(appsignal_data_map_new)
end
def data_array_new
Data.new(appsignal_data_array_new)
end
def running_in_container?
appsignal_running_in_container
end
def set_gauge(key, value, tags)
appsignal_set_gauge(make_appsignal_string(key), value, tags.pointer)
end
def set_host_gauge(key, value)
appsignal_set_host_gauge(make_appsignal_string(key), value)
end
def set_process_gauge(key, value)
appsignal_set_process_gauge(make_appsignal_string(key), value)
end
def increment_counter(key, value, tags)
appsignal_increment_counter(make_appsignal_string(key), value, tags.pointer)
end
def add_distribution_value(key, value, tags)
appsignal_add_distribution_value(make_appsignal_string(key), value, tags.pointer)
end
class Transaction # rubocop:disable Metrics/ClassLength
include StringHelpers
attr_reader :pointer
def initialize(pointer)
@pointer = FFI::AutoPointer.new(
pointer,
Extension.method(:appsignal_free_transaction)
)
end
def start_event(gc_duration_ms)
Extension.appsignal_start_event(pointer, gc_duration_ms)
end
def finish_event(name, title, body, body_format, gc_duration_ms)
case body
when String
method = :appsignal_finish_event
body_arg = make_appsignal_string(body)
when Data
method = :appsignal_finish_event_data
body_arg = body.pointer
else
raise ArgumentError,
"body argument should be a String or Appsignal::Extension::Data"
end
Extension.public_send(
method,
pointer,
make_appsignal_string(name),
make_appsignal_string(title),
body_arg,
body_format,
gc_duration_ms
)
end
def record_event(name, title, body, body_format, duration, gc_duration_ms) # rubocop:disable Metrics/ParameterLists
case body
when String
method = :appsignal_record_event
body_arg = make_appsignal_string(body)
when Data
method = :appsignal_record_event_data
body_arg = body.pointer
else
raise ArgumentError,
"body argument should be a String or Appsignal::Extension::Data"
end
Extension.public_send(
method,
pointer,
make_appsignal_string(name),
make_appsignal_string(title),
body_arg,
body_format,
duration,
gc_duration_ms
)
end
def set_error(name, message, backtrace)
Extension.appsignal_set_transaction_error(
pointer,
make_appsignal_string(name),
make_appsignal_string(message),
backtrace.pointer
)
end
def set_action(action_name) # rubocop:disable Naming/AccessorMethodName
Extension.appsignal_set_transaction_action(
pointer,
make_appsignal_string(action_name)
)
end
def set_namespace(namespace) # rubocop:disable Naming/AccessorMethodName
Extension.appsignal_set_transaction_namespace(
pointer,
make_appsignal_string(namespace)
)
end
def set_sample_data(key, payload)
Extension.appsignal_set_transaction_sample_data(
pointer,
make_appsignal_string(key),
payload.pointer
)
end
def set_queue_start(time) # rubocop:disable Naming/AccessorMethodName
Extension.appsignal_set_transaction_queue_start(pointer, time)
end
def set_metadata(key, value)
Extension.appsignal_set_transaction_metadata(
pointer,
make_appsignal_string(key),
make_appsignal_string(value)
)
end
def finish(gc_duration_ms)
Extension.appsignal_finish_transaction(pointer, gc_duration_ms)
end
def complete
Extension.appsignal_complete_transaction(pointer)
end
def to_json
json = Extension.appsignal_transaction_to_json(pointer)
make_ruby_string(json) if json[:len] > 0
end
end
class Data
include StringHelpers
attr_reader :pointer
def initialize(pointer)
@pointer = FFI::AutoPointer.new(
pointer,
Extension.method(:appsignal_free_data)
)
end
def set_string(key, value)
Extension.appsignal_data_map_set_string(
pointer,
make_appsignal_string(key),
make_appsignal_string(value)
)
end
def set_integer(key, value)
Extension.appsignal_data_map_set_integer(
pointer,
make_appsignal_string(key),
value
)
end
def set_float(key, value)
Extension.appsignal_data_map_set_float(
pointer,
make_appsignal_string(key),
value
)
end
def set_boolean(key, value)
Extension.appsignal_data_map_set_boolean(
pointer,
make_appsignal_string(key),
value
)
end
def set_nil(key) # rubocop:disable Naming/AccessorMethodName
Extension.appsignal_data_map_set_null(
pointer,
make_appsignal_string(key)
)
end
def set_data(key, value)
Extension.appsignal_data_map_set_data(
pointer,
make_appsignal_string(key),
value.pointer
)
end
def append_string(value)
Extension.appsignal_data_array_append_string(
pointer,
make_appsignal_string(value)
)
end
def append_integer(value)
Extension.appsignal_data_array_append_integer(pointer, value)
end
def append_float(value)
Extension.appsignal_data_array_append_float(pointer, value)
end
def append_boolean(value)
Extension.appsignal_data_array_append_boolean(pointer, value)
end
def append_nil
Extension.appsignal_data_array_append_null(pointer)
end
def append_data(value)
Extension.appsignal_data_array_append_data(pointer, value.pointer)
end
def ==(other)
Extension.appsignal_data_equal(pointer, other.pointer)
end
def to_s
make_ruby_string Extension.appsignal_data_to_json(pointer)
end
end
end
end
end