module AppDynamics
@private
Constants
- IGNORE
- VERSION
Public Class Methods
add_snapshot_details(details=nil, &block)
click to toggle source
Add details to the current trace's snapshot. See {Trace#add_snapshot_details} for more details.
# File lib/app_dynamics.rb, line 86 def self.add_snapshot_details(details=nil, &block) return unless instrumenter instrumenter.add_snapshot_details(details, &block) end
after_fork()
click to toggle source
# File lib/app_dynamics.rb, line 63 def self.after_fork instrumenter&.after_fork end
before_fork()
click to toggle source
# File lib/app_dynamics.rb, line 59 def self.before_fork instrumenter&.before_fork end
business_transactions()
click to toggle source
Returns the list of business transactions defined in the active config. @return [BusinessTransactions::TransactionSet, nil]
# File lib/app_dynamics.rb, line 75 def self.business_transactions return unless instrumenter instrumenter.config.get(:business_transactions) end
config_class()
click to toggle source
# File lib/app_dynamics.rb, line 51 def self.config_class Config end
correlation_header()
click to toggle source
Returns the name of the HTTP correlation header @return [String] header name
# File lib/app_dynamics.rb, line 69 def self.correlation_header @correlation_header ||= native_correlation_header end
instrumenter_class()
click to toggle source
# File lib/app_dynamics.rb, line 55 def self.instrumenter_class Instrumenter end
is_snapshotting()
click to toggle source
Check whether the current trace is snapshotting. See {Trace#is_snapshotting} for more details.
# File lib/app_dynamics.rb, line 81 def self.is_snapshotting instrumenter && instrumenter.is_snapshotting end
lex_sql(p1)
click to toggle source
static VALUE lex_sql(VALUE klass) { return Qnil; } static VALUE instrumenter_new(VALUE klass, VALUE rb_config) { return Qnil; } static VALUE instrumenter_start(VALUE self) { return Qnil; } static VALUE instrumenter_start_sdk(VALUE self) { return Qnil; } static VALUE instrumenter_stop(VALUE self) { return Qnil; } static VALUE instrumenter_submit_trace(VALUE self, VALUE rb_trace) { return Qnil; } static VALUE instrumenter_track_desc(VALUE self, VALUE rb_endpoint, VALUE rb_desc) { return Qnil; } static VALUE trace_new(VALUE klass, VALUE start, VALUE uuid, VALUE endpoint, VALUE meta) { return Qnil; } static VALUE trace_get_started_at(VALUE self) { return Qnil; } static VALUE trace_get_endpoint(VALUE self) { return Qnil; } static VALUE trace_set_endpoint(VALUE self, VALUE endpoint) { return Qnil; } static VALUE trace_set_exception(VALUE self, VALUE exception) { return Qnil; } static VALUE trace_get_uuid(VALUE self) { return Qnil; } static VALUE trace_start_span(VALUE self, VALUE time, VALUE category) { return Qnil; } static VALUE trace_stop_span(VALUE self, VALUE span_id, VALUE time) { return Qnil; } static VALUE trace_span_get_category(VALUE self, VALUE span_id) { return Qnil; } static VALUE trace_span_set_title(VALUE self, VALUE span_id, VALUE title) { return Qnil; } static VALUE trace_span_get_title(VALUE self, VALUE span_id) { return Qnil; } static VALUE trace_span_set_description(VALUE self, VALUE span_id, VALUE desc) { return Qnil; } static VALUE trace_span_set_meta(VALUE self, VALUE span_id, VALUE meta) { return Qnil; } static VALUE trace_span_started(VALUE self, VALUE span_id) { return Qnil; } static VALUE trace_span_set_exception(VALUE self, VALUE span_id, VALUE exception, VALUE exception_details) { return Qnil; } static VALUE trace_span_get_correlation_header(VALUE self, VALUE span_id) { return Qnil; } static VALUE trace_is_snapshotting(VALUE self) { return Qnil; } static VALUE trace_add_snapshot_details(VALUE self, VALUE key, VALUE value) { return Qnil; } static VALUE trace_add_snapshot_url(VALUE self, VALUE url) { return Qnil; } static VALUE metrics_define(VALUE self, VALUE name) { return Qnil; } static VALUE metrics_report(VALUE self, VALUE name, VALUE value) { return Qnil; } #endif #ifdef HAVE_APPDYNAMICS_H #include <time.h> #include <stdio.h> #include <inttypes.h> #define TO_S(VAL) \ RSTRING_PTR(rb_funcall(VAL, rb_intern("to_s"), 0)) #define CHECK_TYPE(VAL, T) \ do { \ if (TYPE(VAL) != T) { \ rb_raise(rb_eArgError, "expected " #VAL " to be " #T " but was '%s' (%s [%i])", \ TO_S(VAL), rb_obj_classname(VAL), TYPE(VAL)); \ return Qnil; \ } \ } while(0) #define CHECK_NUMERIC(VAL) \ do { \ if (TYPE(VAL) != T_BIGNUM && \ TYPE(VAL) != T_FIXNUM) { \ rb_raise(rb_eArgError, "expected " #VAL " to be numeric but was '%s' (%s [%i])", \ TO_S(VAL), rb_obj_classname(VAL), TYPE(VAL)); \ return Qnil; \ } \ } while(0) \ #define My_Struct(name, Type, msg) \ Get_Struct(name, self, Type, msg); \ #define Transfer_My_Struct(name, Type, msg) \ My_Struct(name, Type, msg); \ DATA_PTR(self) = NULL; \ #define Transfer_Struct(name, obj, Type, msg) \ Get_Struct(name, obj, Type, msg); \ DATA_PTR(obj) = NULL; \ #define Get_Struct(name, obj, Type, msg) \ Data_Get_Struct(obj, Type, name); \ if (name == NULL) { \ rb_raise(rb_eRuntimeError, "%s", msg); \ } /** * Ruby GVL helpers */ // FIXME: This conditional doesn't logically cover every case #if defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL) && \ defined(HAVE_RUBY_THREAD_H) // Ruby 2.0+ #include <ruby/thread.h> typedef void* (*blocking_fn_t)(void*); #define WITHOUT_GVL(fn, a) \ rb_thread_call_without_gvl((blocking_fn_t)(fn), (a), 0, 0) // Ruby 1.9 #elif defined(HAVE_RB_THREAD_BLOCKING_REGION) typedef VALUE (*blocking_fn_t)(void*); #define WITHOUT_GVL(fn, a) \ rb_thread_blocking_region((blocking_fn_t)(fn), (a), 0, 0) #endif /** * Ruby types defined here */ static const char* existing_instrumenter_msg = "Instrumenter is already running"; static const char* no_instrumenter_msg = "Instrumenter not currently running"; static const char* no_trace_msg = "Trace is not available"; /* * Logging */ #define LOG(target, method, fmt, ...) \ do { \ char* msg; \ int assigned = asprintf(&msg, fmt, ##__VA_ARGS__); \ if (assigned > -1) { \ rb_funcall(target, rb_intern(method), 1, rb_str_new_cstr(msg)); \ } else { \ rb_raise(rb_eRuntimeError, "Unable to log from native"); \ } \ free(msg); \ } while (0) #define TRACE(fmt, ...) \ LOG(self, "log_trace", fmt, ##__VA_ARGS__); #define DEBUG(fmt, ...) \ LOG(self, "log_debug", fmt, ##__VA_ARGS__); #define INFO(fmt, ...) \ LOG(self, "log_info", fmt, ##__VA_ARGS__); #define WARN(fmt, ...) \ LOG(self, "log_warn", fmt, ##__VA_ARGS__); #define ERROR(fmt, ...) \ LOG(self, "log_error", fmt, ##__VA_ARGS__); // FIXME: // - Add more type checking // - Split up methods /* * Returns true if the native SDK is present. */ static VALUE check_native(VALUE klass) { UNUSED(klass); return Qtrue; } /* * AppDynamics correlation header name * @return [String] */ static VALUE get_correlation_header_name(VALUE klass) { UNUSED(klass); return rb_str_new_cstr(APPD_CORRELATION_HEADER_NAME); } /* * Lex SQL queries * @return [String, String] */ static VALUE lex_sql(VALUE klass, VALUE sql) { #ifdef HAVE_LEX_SQL_UNKNOWN sql_lex_result_t result; UNUSED(klass); CHECK_TYPE(sql, T_STRING); result = lex_sql_unknown(StringValueCStr(sql)); if (result.status == 1) { VALUE title = rb_str_new2(result.title); VALUE statement = rb_str_new2(result.statement); free_lex_result(result); return rb_ary_new3(2, title, statement); } else { VALUE exception = rb_exc_new_cstr(rb_eRuntimeError, result.error); free_lex_result(result); rb_exc_raise(exception); return Qnil; } #endif #ifndef HAVE_LEX_SQL_UNKNOWN return Qnil; #endif } /* * * class AppDynamics::Instrumenter * */ static VALUE instrumenter_new(VALUE klass) { sky_instrumenter_t* instrumenter = malloc(sizeof(sky_instrumenter_t)); instrumenter->config = NULL; instrumenter->started = false; instrumenter->num_backends = 0; return Data_Wrap_Struct(klass, NULL, free, instrumenter); } static void* instrumenter_start_nogvl(sky_instrumenter_t* instrumenter) { int initRC; initRC = appd_sdk_init(instrumenter->config); if (initRC) { return (void*) false; } sky_activate_memprof(); instrumenter->started = true; return (void*) true; } static VALUE instrumenter_start_sdk(VALUE self) { sky_instrumenter_t* instrumenter; struct appd_config *config; VALUE rb_config; VALUE rb_config_hash; VALUE app_name; VALUE tier_name; VALUE node_name; VALUE controller_host; VALUE controller_port; VALUE controller_account; VALUE controller_access_key; VALUE controller_use_ssl; VALUE controller_cert_path; VALUE controller_http_proxy_host; VALUE controller_http_proxy_port; VALUE controller_http_proxy_username; VALUE controller_http_proxy_password; VALUE controller_http_proxy_password_file; VALUE controller_log_dir; VALUE controller_log_level; VALUE controller_log_max_num_files; VALUE controller_log_max_file_size; VALUE init_timeout_ms; VALUE logger; My_Struct(instrumenter, sky_instrumenter_t, no_instrumenter_msg); if (instrumenter->started) { rb_raise(rb_eRuntimeError, "%s", existing_instrumenter_msg); } // FIXME: Does the extra step of converting to a hash make sense here? rb_config = rb_iv_get(self, "@config"); rb_config_hash = rb_funcall(rb_config, rb_intern("to_native_hash"), 0); logger = rb_hash_aref(rb_config_hash, ID2SYM(rb_intern("logger"))); config = appd_config_init(); // FIXME: This could definitely be more elegant, maybe make new class to wrap appd_config // FIXME: Add Windows proxy settings app_name = rb_hash_aref(rb_config_hash, ID2SYM(rb_intern("app_name"))); CHECK_TYPE(app_name, T_STRING); LOG(logger, "info", "App Name: %s", StringValueCStr(app_name)); tier_name = rb_hash_aref(rb_config_hash, ID2SYM(rb_intern("tier_name"))); CHECK_TYPE(tier_name, T_STRING); LOG(logger, "info", "Tier Name: %s", StringValueCStr(tier_name)); node_name = rb_hash_aref(rb_config_hash, ID2SYM(rb_intern("node_name"))); CHECK_TYPE(node_name, T_STRING); LOG(logger, "info", "Node Name: %s", StringValueCStr(node_name)); controller_host = rb_hash_aref(rb_config_hash, ID2SYM(rb_intern("controller.host"))); CHECK_TYPE(controller_host, T_STRING); controller_port = rb_hash_aref(rb_config_hash, ID2SYM(rb_intern("controller.port"))); CHECK_TYPE(controller_port, T_FIXNUM); controller_account = rb_hash_aref(rb_config_hash, ID2SYM(rb_intern("controller.account"))); CHECK_TYPE(controller_account, T_STRING); controller_access_key = rb_hash_aref(rb_config_hash, ID2SYM(rb_intern("controller.access_key"))); CHECK_TYPE(controller_access_key, T_STRING); controller_use_ssl = rb_hash_aref(rb_config_hash, ID2SYM(rb_intern("controller.use_ssl"))); controller_cert_path = rb_hash_aref(rb_config_hash, ID2SYM(rb_intern("controller.cert_path"))); controller_http_proxy_host = rb_hash_aref(rb_config_hash, ID2SYM(rb_intern("controller.http_proxy_host"))); controller_http_proxy_port = rb_hash_aref(rb_config_hash, ID2SYM(rb_intern("controller.http_proxy_port"))); controller_http_proxy_username = rb_hash_aref(rb_config_hash, ID2SYM(rb_intern("controller.http_proxy_username"))); controller_http_proxy_password = rb_hash_aref(rb_config_hash, ID2SYM(rb_intern("controller.http_proxy_password"))); controller_http_proxy_password_file = rb_hash_aref(rb_config_hash, ID2SYM(rb_intern("controller.http_proxy_password_file"))); controller_log_dir = rb_hash_aref(rb_config_hash, ID2SYM(rb_intern("controller.log_dir"))); controller_log_level = rb_hash_aref(rb_config_hash, ID2SYM(rb_intern("controller.log_level"))); controller_log_max_num_files = rb_hash_aref(rb_config_hash, ID2SYM(rb_intern("controller.log_max_num_files"))); controller_log_max_file_size = rb_hash_aref(rb_config_hash, ID2SYM(rb_intern("controller.log_max_file_size"))); init_timeout_ms = rb_hash_aref(rb_config_hash, ID2SYM(rb_intern("init_timeout_ms"))); CHECK_TYPE(init_timeout_ms, T_FIXNUM); appd_config_set_app_name(config, StringValueCStr(app_name)); appd_config_set_tier_name(config, StringValueCStr(tier_name)); appd_config_set_node_name(config, StringValueCStr(node_name)); appd_config_set_controller_host(config, StringValueCStr(controller_host)); appd_config_set_controller_port(config, FIX2UINT(controller_port)); appd_config_set_controller_account(config, StringValueCStr(controller_account)); appd_config_set_controller_access_key(config, StringValueCStr(controller_access_key)); appd_config_set_controller_use_ssl(config, RTEST(controller_use_ssl) ? 1 : 0); appd_config_set_logging_log_dir(config, StringValueCStr(controller_log_dir)); if (controller_log_level != Qnil) { appd_config_set_logging_min_level(config, FIX2UINT(controller_log_level)); } if (controller_log_max_num_files != Qnil) { appd_config_set_logging_max_num_files(config, FIX2UINT(controller_log_max_num_files)); } if (controller_log_max_file_size != Qnil) { appd_config_set_logging_max_file_size_bytes(config, FIX2UINT(controller_log_max_file_size)); } appd_config_set_init_timeout_ms(config, FIX2UINT(init_timeout_ms)); if (controller_cert_path != Qnil) { if (TYPE(controller_cert_path) == T_STRING) { LOG(logger, "info", "Setting cert path: %s", StringValueCStr(controller_cert_path)); appd_config_set_controller_certificate_file(config, StringValueCStr(controller_cert_path)); } else { LOG(logger, "warn", "Ignoring cert path, invalid type"); } } if (controller_http_proxy_host != Qnil) { if (TYPE(controller_http_proxy_host) == T_STRING) { LOG(logger, "info", "Setting HTTP proxy host: %s", StringValueCStr(controller_http_proxy_host)); appd_config_set_controller_http_proxy_host(config, StringValueCStr(controller_http_proxy_host)); } else { LOG(logger, "warn", "Ignoring HTTP proxy host, invalid type"); } } if (controller_http_proxy_port != Qnil) { if (TYPE(controller_http_proxy_port) == T_FIXNUM) { LOG(logger, "info", "Setting HTTP proxy port: %i", FIX2UINT(controller_http_proxy_port)); appd_config_set_controller_http_proxy_port(config, FIX2UINT(controller_http_proxy_port)); } else { LOG(logger, "warn", "Ignoring HTTP proxy port, invalid type"); } } if (controller_http_proxy_username != Qnil) { if (TYPE(controller_http_proxy_username) == T_STRING) { LOG(logger, "info", "Setting HTTP proxy username: %s", StringValueCStr(controller_http_proxy_username)); appd_config_set_controller_http_proxy_username(config, StringValueCStr(controller_http_proxy_username)); } else { LOG(logger, "warn", "Ignoring HTTP proxy username, invalid type"); } } if (controller_http_proxy_password != Qnil) { if (TYPE(controller_http_proxy_password) == T_STRING) { LOG(logger, "info", "Setting HTTP proxy password"); appd_config_set_controller_http_proxy_password(config, StringValueCStr(controller_http_proxy_password)); } else { LOG(logger, "warn", "Ignoring HTTP proxy password, invalid type"); } } if (controller_http_proxy_password_file != Qnil) { if (TYPE(controller_http_proxy_password_file) == T_STRING) { LOG(logger, "info", "Setting HTTP proxy password file: %s", StringValueCStr(controller_http_proxy_password_file)); appd_config_set_controller_http_proxy_password_file(config, StringValueCStr(controller_http_proxy_password_file)); } else { LOG(logger, "warn", "Ignoring HTTP proxy password file, invalid type"); } } instrumenter->config = config; if (WITHOUT_GVL(instrumenter_start_nogvl, instrumenter)) { INFO("Initialized AppDynamics SDK"); instrumenter->started = true; return Qtrue; } else { ERROR("Unable to initialize AppDynamics SDK"); return Qfalse; } } // TODO: Revist this since it doesn't do much static VALUE instrumenter_start(VALUE self) { sky_instrumenter_t* instrumenter; My_Struct(instrumenter, sky_instrumenter_t, no_instrumenter_msg); if (instrumenter->started) { rb_raise(rb_eRuntimeError, "%s", existing_instrumenter_msg); } return Qtrue; } static VALUE instrumenter_stop(VALUE self) { sky_instrumenter_t* instrumenter; My_Struct(instrumenter, sky_instrumenter_t, no_instrumenter_msg); if (instrumenter->started) { INFO("Terminating AppDynamics SDK"); sky_deactivate_memprof(); TRACE("appd_sdk_term before"); appd_sdk_term(); instrumenter->started = false; DEBUG("Terminated AppDynamics SDK"); } return Qnil; } int sort_span_duration_desc(const void *a, const void *b) { sky_span_t *span_a = *(sky_span_t **)a; sky_span_t *span_b = *(sky_span_t **)b; long long a_dur = span_a->stop - span_a->start; long long b_dur = span_b->stop - span_b->start; return (int)(b_dur - a_dur); } static VALUE instrumenter_submit_trace(VALUE self, VALUE rb_trace) { sky_trace_t* trace; uint i = 0; Transfer_Struct(trace, rb_trace, sky_trace_t, no_trace_msg); if (sky_have_memprof()) { trace->allocations = sky_consume_allocations(); } TRACE("start: %llu", trace->start); TRACE("endpoint: %s", trace->endpoint); if (sky_have_memprof()) { TRACE("allocations: %" PRIu64, trace->allocations); } TRACE("num_spans: %d", trace->num_spans); TRACE("spans:"); // TODO: Only iterate here if tracing for (i=0; i < trace->num_spans; i++) { sky_span_t *span = trace->spans[i]; TRACE(" %u:", i); TRACE(" is_exitcall: %i", span->is_exitcall); TRACE(" start: %llu", span->start); TRACE(" stop: %llu", span->stop); TRACE(" category: %s", span->category); TRACE(" title: %s", span->title ? span->title : "-"); TRACE(" description: %s", span->description ? span->description : "-"); } if (appd_bt_is_snapshotting(trace->handle)) { sky_span_t *gc_span = NULL; sky_span_t **sql_spans = malloc(trace->num_spans * sizeof(sky_span_t*)); uint num_sql_spans = 0; TRACE("Logging additional snapshot details"); // Set starting URL if (trace->request_url != NULL) { LOG(rb_mAppDynamics, "log_debug", "Setting URL: %s", trace->request_url); appd_bt_set_url(trace->handle, trace->request_url); } if (sky_have_memprof()) { char buffer[20]; // 64-bit number is 19 chars max sprintf(buffer, "%" PRIu64, trace->allocations); appd_bt_add_user_data(trace->handle, "Objects Allocated", buffer); } // Find GC and SQL spans for (i=0; i < trace->num_spans; i++) { sky_span_t *span = trace->spans[i]; if (strcmp(span->category, "db.sql.query") == 0) { sql_spans[num_sql_spans++] = span; } else if (strcmp(span->category, "noise.gc") == 0) { gc_span = span; } } if (gc_span != NULL) { // Log GC details in snapshot char value[25]; // More than enough for duration float duration; duration = (float)(gc_span->stop - gc_span->start) / 10; sprintf(value, "%.2f ms", duration); TRACE("GC Time: %s", value); appd_bt_add_user_data(trace->handle, "GC Time", value); } // Sort SQL spans in reverse duration order qsort(sql_spans, num_sql_spans, sizeof(sky_span_t*), sort_span_duration_desc); // Log details about each SQL statement in snapshot for (i=0; i < num_sql_spans && i < 10; i++) { char key[25]; // Allows for 18 string chars and up to 7 numbers, more than enough char *value = NULL; float duration; int aspfRC; sky_span_t *span = sql_spans[i]; sprintf(key, "Top SQL statement %i", i+1); duration = (float)(span->stop - span->start) / 10; aspfRC = asprintf(&value, "%.2f ms: %s", duration, span->description); if (aspfRC > -1) { TRACE("%s: %s", key, value); appd_bt_add_user_data(trace->handle, key, value); } else { ERROR("asprintf failed; unable to log slow SQL statement"); } free(value); } free(sql_spans); } else { TRACE("Not snapshotting, won't log extra details"); } TRACE("Ending BT; endpoint=%s; handle=%p", trace->endpoint, trace->handle); appd_bt_end(trace->handle); return Qnil; } static VALUE instrumenter_track_desc(VALUE self, VALUE rb_endpoint, VALUE rb_desc) { UNUSED(self); UNUSED(rb_endpoint); UNUSED(rb_desc); return Qtrue; } /* * * class AppDynamics::Trace * */ void trace_mark(sky_trace_t *trace) { uint i; if (trace->spans != NULL) { for (i = 0; i < trace->num_spans; i++) { sky_span_t *span = trace->spans[i]; // This is a Ruby object, so mark it to show that we care about it rb_gc_mark(span->meta); } } } void trace_free(sky_trace_t *trace) { uint i; free(trace->endpoint); free(trace->request_url); for (i = 0; i < trace->num_spans; i++) { sky_span_t *span = trace->spans[i]; free(span->category); free(span->title); free(span->description); // No need to free meta since it is a Ruby object, but we do need to mark it free(span); } free(trace->spans); free(trace); } static VALUE trace_new(VALUE klass, VALUE start, VALUE uuid, VALUE endpoint, VALUE meta) { sky_trace_t *trace = malloc(sizeof(sky_trace_t)); char *uuid_str = NULL; char *endpoint_str = NULL; char *correlation_header = NULL; char *request_url = NULL; char *tmp = NULL; bool ignored; UNUSED(klass); CHECK_NUMERIC(start); CHECK_TYPE(endpoint, T_STRING); if (meta != Qnil) { VALUE rb_correlation_header; VALUE rb_request_url; CHECK_TYPE(meta, T_HASH); // Could/should the uuid be used for the correlation header? rb_correlation_header = rb_hash_aref(meta, ID2SYM(rb_intern("correlation_header"))); if (TYPE(rb_correlation_header) == T_STRING) { correlation_header = StringValueCStr(rb_correlation_header); } rb_request_url = rb_hash_aref(meta, ID2SYM(rb_intern("request_url"))); if (TYPE(rb_request_url) == T_STRING) { tmp = StringValueCStr(rb_request_url); request_url = malloc(sizeof(char) * strlen(tmp) + 1); strcpy(request_url, tmp); } } uuid_str = StringValueCStr(uuid); endpoint_str = StringValueCStr(endpoint); ignored = (strcmp(endpoint_str, IGNORE) == 0); LOG(rb_mAppDynamics, "log_debug", "trace=%s; BT Begin: %s, %s, ignored=%d", uuid_str, endpoint_str, correlation_header, ignored); if (ignored) { trace->handle = NULL; trace->ignored = true; } else { trace->handle = appd_bt_begin(endpoint_str, correlation_header); trace->ignored = false; LOG(rb_mAppDynamics, "log_trace", "trace=%s; BT handle=%p", uuid_str, trace->handle); if (trace->handle == NULL) { LOG(rb_mAppDynamics, "log_warn", "trace=%s; Unable to obtain a BT handle, request will not be tracked.", uuid_str); } } trace->start = NUM2ULL(start); tmp = StringValueCStr(endpoint); trace->endpoint = malloc(sizeof(char) * strlen(tmp) + 1); strcpy(trace->endpoint, tmp); trace->request_url = request_url; // Reset allocations counter if (sky_have_memprof()) { sky_consume_allocations(); } trace->num_spans = 0; trace->spans = NULL; return Data_Wrap_Struct(klass, trace_mark, trace_free, trace); } static VALUE trace_get_started_at(VALUE self) { sky_trace_t* trace; My_Struct(trace, sky_trace_t, no_trace_msg); return ULL2NUM(trace->start); } static VALUE trace_get_endpoint(VALUE self) { sky_trace_t* trace; My_Struct(trace, sky_trace_t, no_trace_msg); return rb_str_new_cstr(trace->endpoint); } // AppDynamics doesn't allow us to set after the fact static VALUE trace_set_endpoint(VALUE self, VALUE endpoint) { return Qnil; } static VALUE trace_set_exception(VALUE self, VALUE exception) { sky_trace_t* trace; VALUE exception_str; char* error_name; bool mark_bt_as_error; My_Struct(trace, sky_trace_t, no_trace_msg); if (trace->ignored) { return Qnil; } exception_str = rb_funcall(exception, rb_intern("to_s"), 0); error_name = StringValueCStr(exception_str); mark_bt_as_error = true; DEBUG("Adding error: %s", error_name); appd_bt_add_error(trace->handle, APPD_LEVEL_ERROR, error_name, mark_bt_as_error); return Qnil; } static VALUE trace_get_uuid(VALUE self) { return Qnil; } static VALUE trace_start_span(VALUE self, VALUE time, VALUE category) { sky_trace_t* trace; sky_span_t* span = malloc(sizeof(sky_span_t)); uint span_id; char* tmp; My_Struct(trace, sky_trace_t, no_trace_msg); if (trace->ignored) { return Qnil; } CHECK_NUMERIC(time); CHECK_TYPE(category, T_STRING); span->is_exitcall = false; span->start = NUM2ULL(time); tmp = StringValueCStr(category); span->category = malloc(sizeof(char) * strlen(tmp) + 1); strcpy(span->category, tmp); span->title = NULL; span->description = NULL; span->meta = Qnil; span_id = trace->num_spans; if (span_id % SPAN_BATCH == 0) { if (span_id >= MAX_SPANS) { rb_raise(rb_eRuntimeError, "Exceeded maximum number of spans (%d)", MAX_SPANS); } TRACE("Growing spans size to %i", span_id+SPAN_BATCH); trace->spans = realloc(trace->spans, (span_id+SPAN_BATCH) * sizeof(sky_span_t*)); } trace->spans[span_id] = span; trace->num_spans++; return UINT2NUM(span_id); } static VALUE trace_stop_span(VALUE self, VALUE span_id, VALUE time) { sky_trace_t* trace; sky_span_t* span; UNUSED(self); My_Struct(trace, sky_trace_t, no_trace_msg); if (trace->ignored) { return Qnil; } CHECK_TYPE(span_id, T_FIXNUM); CHECK_NUMERIC(time); // In theory we should check that the spans are closed in order span = trace->spans[FIX2UINT(span_id)]; span->stop = NUM2ULL(time); if (span->is_exitcall) { char *title; int rc; title = span->title ? span->title : span->category; DEBUG("Setting exitcall title: %s", title); rc = appd_exitcall_set_details(span->handle, title); if (rc) { ERROR("Failed to set exitcall details; %s; err=%i", title, rc); } appd_exitcall_end(span->handle); } return Qnil; } static VALUE trace_span_get_category(VALUE self, VALUE span_id) { sky_trace_t* trace; sky_span_t* span; My_Struct(trace, sky_trace_t, no_trace_msg); if (trace->ignored) { return Qnil; } CHECK_TYPE(span_id, T_FIXNUM); span = trace->spans[FIX2UINT(span_id)]; return rb_str_new_cstr(span->category); } static VALUE trace_span_set_title(VALUE self, VALUE span_id, VALUE title) { sky_trace_t* trace; sky_span_t* span; char *tmp; My_Struct(trace, sky_trace_t, no_trace_msg); if (trace->ignored) { return Qnil; } CHECK_TYPE(span_id, T_FIXNUM); CHECK_TYPE(title, T_STRING); span = trace->spans[FIX2UINT(span_id)]; tmp = StringValueCStr(title); span->title = malloc(sizeof(char) * strlen(tmp) + 1); strcpy(span->title, tmp); return Qnil; } static VALUE trace_span_get_title(VALUE self, VALUE span_id) { sky_trace_t* trace; sky_span_t* span; My_Struct(trace, sky_trace_t, no_trace_msg); if (trace->ignored) { return Qnil; } CHECK_TYPE(span_id, T_FIXNUM); span = trace->spans[FIX2UINT(span_id)]; return span->title ? rb_str_new_cstr(span->title) : Qnil; } static VALUE trace_span_set_description(VALUE self, VALUE span_id, VALUE desc) { sky_trace_t* trace; sky_span_t* span; char *tmp; My_Struct(trace, sky_trace_t, no_trace_msg); if (trace->ignored) { return Qnil; } CHECK_TYPE(span_id, T_FIXNUM); CHECK_TYPE(desc, T_STRING); span = trace->spans[FIX2UINT(span_id)]; tmp = StringValueCStr(desc); span->description = malloc(sizeof(char) * strlen(tmp) + 1); strcpy(span->description, tmp); return Qnil; } static VALUE trace_span_set_meta(VALUE self, VALUE span_id, VALUE meta) { sky_trace_t* trace; sky_span_t* span; My_Struct(trace, sky_trace_t, no_trace_msg); if (trace->ignored) { return Qnil; } CHECK_TYPE(span_id, T_FIXNUM); CHECK_TYPE(meta, T_HASH); span = trace->spans[FIX2UINT(span_id)]; span->meta = meta; return Qnil; } static VALUE trace_span_started(VALUE self, VALUE span_id) { sky_trace_t* trace; sky_span_t* span; VALUE rb_mAppDynamics; VALUE rb_mBackend; VALUE rb_iBackendSub; My_Struct(trace, sky_trace_t, no_trace_msg); if (trace->ignored) { return Qnil; } CHECK_TYPE(span_id, T_FIXNUM); span = trace->spans[FIX2UINT(span_id)]; rb_mAppDynamics = rb_define_module("AppDynamics"); rb_mBackend = rb_define_module_under(rb_mAppDynamics, "Backend"); rb_iBackendSub = rb_funcall(rb_mBackend, rb_intern("build"), 4, rb_str_new_cstr(span->category), span->title ? rb_str_new_cstr(span->title) : Qnil, span->description ? rb_str_new_cstr(span->description) : Qnil, span->meta); if (rb_iBackendSub != Qnil) { VALUE rb_oInstrumenter; sky_instrumenter_t* instrumenter; VALUE rb_backend_name; char* backend_name; bool has_backend; uint i; int rc; DEBUG("Attempting to treat as an exit call"); // TODO: We can streamline this by maintining references in the C-code rb_oInstrumenter = rb_iv_get(self, "@instrumenter"); Get_Struct(instrumenter, rb_oInstrumenter, sky_instrumenter_t, "missing native instrumenter"); // FIXME: Maybe this should include the type too rb_backend_name = rb_funcall(rb_iBackendSub, rb_intern("backend_name"), 0); backend_name = StringValueCStr(rb_backend_name); has_backend = false; for (i = 0; i < instrumenter->num_backends; i++) { if (strcmp(instrumenter->backends[i], backend_name) == 0) { has_backend = true; break; } } if (has_backend) { TRACE("Backend already added: %s", backend_name); } else { int backend_idx; VALUE backend_type; VALUE identifying_properties; uint ipc; DEBUG("Adding backend: %s", backend_name); backend_type = rb_funcall(rb_iBackendSub, rb_intern("backend_type"), 0); appd_backend_declare(StringValueCStr(backend_type), backend_name); identifying_properties = rb_funcall(rb_iBackendSub, rb_intern("identifying_properties_array"), 0); ipc = (int) RARRAY_LEN(identifying_properties); for (i = 0; i < ipc; i += 2) { VALUE rb_key = rb_ary_entry(identifying_properties, i); VALUE rb_val = rb_ary_entry(identifying_properties, i+1); char *key = StringValueCStr(rb_key); char *val = StringValueCStr(rb_val); DEBUG("Identifying: %s - %s\n", key, val); rc = appd_backend_set_identifying_property(backend_name, key, val); if (rc) { ERROR("Unable to set identifying property for backend: %s; key=%s; val=%s; err=%d.", backend_name, key, val, rc); } } backend_idx = instrumenter->num_backends++; strcpy(instrumenter->backends[backend_idx], backend_name); } rc = appd_backend_add(backend_name); if (rc) { ERROR("Unable to add backend: %s; err=%d.", backend_name, rc); } TRACE("Starting exit call; endpoint=%s; backend=%s", trace->endpoint, backend_name); span->is_exitcall = true; span->handle = appd_exitcall_begin(trace->handle, backend_name); TRACE("exit call handle=%p", span->handle); } return Qnil; } static VALUE trace_span_set_exception(VALUE self, VALUE span_id, VALUE exception, VALUE exception_details) { sky_trace_t* trace; sky_span_t* span; VALUE exception_str; char* error_name; bool mark_bt_as_error; My_Struct(trace, sky_trace_t, no_trace_msg); if (trace->ignored) { return Qnil; } span = trace->spans[FIX2UINT(span_id)]; if (span->is_exitcall) { if (exception != Qnil) { exception_str = rb_funcall(exception, rb_intern("to_s"), 0); } else { exception_str = rb_funcall(exception_details, rb_intern("join"), rb_str_new_cstr(", ")); } error_name = StringValueCStr(exception_str); // We'll report the bt error separately mark_bt_as_error = false; DEBUG("Adding span error: %s\n", error_name); appd_exitcall_add_error(span->handle, APPD_LEVEL_ERROR, error_name, mark_bt_as_error); } return Qnil; } static VALUE trace_span_get_correlation_header(VALUE self, VALUE span_id) { sky_trace_t* trace; sky_span_t* span; My_Struct(trace, sky_trace_t, no_trace_msg); if (trace->ignored) { return Qnil; } span = trace->spans[FIX2UINT(span_id)]; if (span->is_exitcall) { return rb_str_new_cstr(appd_exitcall_get_correlation_header(span->handle)); } else { return Qnil; } } static VALUE trace_is_snapshotting(VALUE self) { sky_trace_t* trace; My_Struct(trace, sky_trace_t, no_trace_msg); if (trace->ignored) { return Qfalse; } if (appd_bt_is_snapshotting(trace->handle)) { return Qtrue; } else { return Qtrue; } } static VALUE trace_add_snapshot_details(VALUE self, VALUE key, VALUE value) { sky_trace_t* trace; char* c_key; char* c_value; My_Struct(trace, sky_trace_t, no_trace_msg); if (trace->ignored) { return Qnil; } c_key = StringValueCStr(key); c_value = StringValueCStr(value); appd_bt_add_user_data(trace->handle, c_key, c_value); return Qnil; } static VALUE trace_add_snapshot_url(VALUE self, VALUE url) { sky_trace_t* trace; char* c_url; My_Struct(trace, sky_trace_t, no_trace_msg); if (trace->ignored) { return Qnil; } c_url = StringValueCStr(url); appd_bt_set_url(trace->handle, c_url); return Qnil; } // FIXME: Remove duplication in next two methods static VALUE metrics_define(VALUE self, VALUE name) { char* metric_name; int rc = asprintf(&metric_name, "Custom Metrics|Ruby|%s", StringValueCStr(name)); if (rc > -1) { TRACE("Registering metric: %s", metric_name) appd_custom_metric_add(NULL, metric_name, APPD_TIMEROLLUP_TYPE_AVERAGE, APPD_CLUSTERROLLUP_TYPE_INDIVIDUAL, APPD_HOLEHANDLING_TYPE_RATE_COUNTER); } else { ERROR("Unable to allocate metric_name"); } free(metric_name); return Qnil; } static VALUE metrics_report(VALUE self, VALUE name, VALUE value) { char* metric_name; int rc = asprintf(&metric_name, "Custom Metrics|Ruby|%s", StringValueCStr(name)); if (rc > -1) { unsigned long c_value = NUM2ULONG(value); TRACE("Reporting metric: %s: %lu", metric_name, c_value); appd_custom_metric_report(NULL, metric_name, c_value); } else { ERROR("Unable to allocate metric_name"); } free(metric_name); return Qnil; } #endif void Init_app_dynamics_native() { VALUE rb_cTrace; VALUE rb_cInstrumenter; VALUE rb_cBackgroundMetrics; rb_mAppDynamics = rb_define_module("AppDynamics"); // AppDynamics custom methods rb_define_singleton_method(rb_mAppDynamics, "native?", check_native, 0); rb_define_singleton_method(rb_mAppDynamics, "native_correlation_header", get_correlation_header_name, 0); rb_define_singleton_method(rb_mAppDynamics, "lex_sql", lex_sql, 1); rb_cTrace = rb_const_get(rb_mAppDynamics, rb_intern("Trace")); // Skylight required methods rb_define_singleton_method(rb_cTrace, "native_new", trace_new, 4); rb_define_method(rb_cTrace, "native_get_started_at", trace_get_started_at, 0); rb_define_method(rb_cTrace, "native_get_endpoint", trace_get_endpoint, 0); rb_define_method(rb_cTrace, "native_set_endpoint", trace_set_endpoint, 1); rb_define_method(rb_cTrace, "native_set_exception", trace_set_exception, 1); rb_define_method(rb_cTrace, "native_get_uuid", trace_get_uuid, 0); rb_define_method(rb_cTrace, "native_start_span", trace_start_span, 2); rb_define_method(rb_cTrace, "native_stop_span", trace_stop_span, 2); rb_define_method(rb_cTrace, "native_span_get_category", trace_span_get_category, 1); rb_define_method(rb_cTrace, "native_span_set_title", trace_span_set_title, 2); rb_define_method(rb_cTrace, "native_span_get_title", trace_span_get_title, 1); rb_define_method(rb_cTrace, "native_span_set_description", trace_span_set_description, 2); rb_define_method(rb_cTrace, "native_span_set_meta", trace_span_set_meta, 2); rb_define_method(rb_cTrace, "native_span_started", trace_span_started, 1); rb_define_method(rb_cTrace, "native_span_set_exception", trace_span_set_exception, 3); rb_define_method(rb_cTrace, "native_span_get_correlation_header", trace_span_get_correlation_header, 1); // AppDynamics custom methods rb_define_method(rb_cTrace, "native_is_snapshotting", trace_is_snapshotting, 0); rb_define_method(rb_cTrace, "native_add_snapshot_details", trace_add_snapshot_details, 2); rb_define_method(rb_cTrace, "native_set_snapshot_url", trace_add_snapshot_url, 1); rb_cInstrumenter = rb_const_get(rb_mAppDynamics, rb_intern("Instrumenter")); // Skylight required methods rb_define_singleton_method(rb_cInstrumenter, "native_new", instrumenter_new, 0); rb_define_method(rb_cInstrumenter, "native_start", instrumenter_start, 0); rb_define_method(rb_cInstrumenter, "native_start_sdk", instrumenter_start_sdk, 0); rb_define_method(rb_cInstrumenter, "native_stop", instrumenter_stop, 0); rb_define_method(rb_cInstrumenter, "native_submit_trace", instrumenter_submit_trace, 1); rb_define_method(rb_cInstrumenter, "native_track_desc", instrumenter_track_desc, 2); // AppDynamics custom methods rb_cBackgroundMetrics = rb_const_get(rb_mAppDynamics, rb_intern("BackgroundMetrics")); rb_define_method(rb_cBackgroundMetrics, "native_define_metric", metrics_define, 1); rb_define_method(rb_cBackgroundMetrics, "native_report_metric", metrics_report, 2); }
native?()
click to toggle source
static VALUE check_native(VALUE klass) { return Qfalse; } static VALUE get_correlation_header_name(VALUE klass) { return Qnil; } static VALUE lex_sql(VALUE klass) { return Qnil; } static VALUE instrumenter_new(VALUE klass, VALUE rb_config) { return Qnil; } static VALUE instrumenter_start(VALUE self) { return Qnil; } static VALUE instrumenter_start_sdk(VALUE self) { return Qnil; } static VALUE instrumenter_stop(VALUE self) { return Qnil; } static VALUE instrumenter_submit_trace(VALUE self, VALUE rb_trace) { return Qnil; } static VALUE instrumenter_track_desc(VALUE self, VALUE rb_endpoint, VALUE rb_desc) { return Qnil; } static VALUE trace_new(VALUE klass, VALUE start, VALUE uuid, VALUE endpoint, VALUE meta) { return Qnil; } static VALUE trace_get_started_at(VALUE self) { return Qnil; } static VALUE trace_get_endpoint(VALUE self) { return Qnil; } static VALUE trace_set_endpoint(VALUE self, VALUE endpoint) { return Qnil; } static VALUE trace_set_exception(VALUE self, VALUE exception) { return Qnil; } static VALUE trace_get_uuid(VALUE self) { return Qnil; } static VALUE trace_start_span(VALUE self, VALUE time, VALUE category) { return Qnil; } static VALUE trace_stop_span(VALUE self, VALUE span_id, VALUE time) { return Qnil; } static VALUE trace_span_get_category(VALUE self, VALUE span_id) { return Qnil; } static VALUE trace_span_set_title(VALUE self, VALUE span_id, VALUE title) { return Qnil; } static VALUE trace_span_get_title(VALUE self, VALUE span_id) { return Qnil; } static VALUE trace_span_set_description(VALUE self, VALUE span_id, VALUE desc) { return Qnil; } static VALUE trace_span_set_meta(VALUE self, VALUE span_id, VALUE meta) { return Qnil; } static VALUE trace_span_started(VALUE self, VALUE span_id) { return Qnil; } static VALUE trace_span_set_exception(VALUE self, VALUE span_id, VALUE exception, VALUE exception_details) { return Qnil; } static VALUE trace_span_get_correlation_header(VALUE self, VALUE span_id) { return Qnil; } static VALUE trace_is_snapshotting(VALUE self) { return Qnil; } static VALUE trace_add_snapshot_details(VALUE self, VALUE key, VALUE value) { return Qnil; } static VALUE trace_add_snapshot_url(VALUE self, VALUE url) { return Qnil; } static VALUE metrics_define(VALUE self, VALUE name) { return Qnil; } static VALUE metrics_report(VALUE self, VALUE name, VALUE value) { return Qnil; } #endif #ifdef HAVE_APPDYNAMICS_H #include <time.h> #include <stdio.h> #include <inttypes.h> #define TO_S(VAL) \ RSTRING_PTR(rb_funcall(VAL, rb_intern("to_s"), 0)) #define CHECK_TYPE(VAL, T) \ do { \ if (TYPE(VAL) != T) { \ rb_raise(rb_eArgError, "expected " #VAL " to be " #T " but was '%s' (%s [%i])", \ TO_S(VAL), rb_obj_classname(VAL), TYPE(VAL)); \ return Qnil; \ } \ } while(0) #define CHECK_NUMERIC(VAL) \ do { \ if (TYPE(VAL) != T_BIGNUM && \ TYPE(VAL) != T_FIXNUM) { \ rb_raise(rb_eArgError, "expected " #VAL " to be numeric but was '%s' (%s [%i])", \ TO_S(VAL), rb_obj_classname(VAL), TYPE(VAL)); \ return Qnil; \ } \ } while(0) \ #define My_Struct(name, Type, msg) \ Get_Struct(name, self, Type, msg); \ #define Transfer_My_Struct(name, Type, msg) \ My_Struct(name, Type, msg); \ DATA_PTR(self) = NULL; \ #define Transfer_Struct(name, obj, Type, msg) \ Get_Struct(name, obj, Type, msg); \ DATA_PTR(obj) = NULL; \ #define Get_Struct(name, obj, Type, msg) \ Data_Get_Struct(obj, Type, name); \ if (name == NULL) { \ rb_raise(rb_eRuntimeError, "%s", msg); \ } /** * Ruby GVL helpers */ // FIXME: This conditional doesn't logically cover every case #if defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL) && \ defined(HAVE_RUBY_THREAD_H) // Ruby 2.0+ #include <ruby/thread.h> typedef void* (*blocking_fn_t)(void*); #define WITHOUT_GVL(fn, a) \ rb_thread_call_without_gvl((blocking_fn_t)(fn), (a), 0, 0) // Ruby 1.9 #elif defined(HAVE_RB_THREAD_BLOCKING_REGION) typedef VALUE (*blocking_fn_t)(void*); #define WITHOUT_GVL(fn, a) \ rb_thread_blocking_region((blocking_fn_t)(fn), (a), 0, 0) #endif /** * Ruby types defined here */ static const char* existing_instrumenter_msg = "Instrumenter is already running"; static const char* no_instrumenter_msg = "Instrumenter not currently running"; static const char* no_trace_msg = "Trace is not available"; /* * Logging */ #define LOG(target, method, fmt, ...) \ do { \ char* msg; \ int assigned = asprintf(&msg, fmt, ##__VA_ARGS__); \ if (assigned > -1) { \ rb_funcall(target, rb_intern(method), 1, rb_str_new_cstr(msg)); \ } else { \ rb_raise(rb_eRuntimeError, "Unable to log from native"); \ } \ free(msg); \ } while (0) #define TRACE(fmt, ...) \ LOG(self, "log_trace", fmt, ##__VA_ARGS__); #define DEBUG(fmt, ...) \ LOG(self, "log_debug", fmt, ##__VA_ARGS__); #define INFO(fmt, ...) \ LOG(self, "log_info", fmt, ##__VA_ARGS__); #define WARN(fmt, ...) \ LOG(self, "log_warn", fmt, ##__VA_ARGS__); #define ERROR(fmt, ...) \ LOG(self, "log_error", fmt, ##__VA_ARGS__); // FIXME: // - Add more type checking // - Split up methods /* * Returns true if the native SDK is present. */ static VALUE check_native(VALUE klass) { UNUSED(klass); return Qtrue; } /* * AppDynamics correlation header name * @return [String] */ static VALUE get_correlation_header_name(VALUE klass) { UNUSED(klass); return rb_str_new_cstr(APPD_CORRELATION_HEADER_NAME); } /* * Lex SQL queries * @return [String, String] */ static VALUE lex_sql(VALUE klass, VALUE sql) { #ifdef HAVE_LEX_SQL_UNKNOWN sql_lex_result_t result; UNUSED(klass); CHECK_TYPE(sql, T_STRING); result = lex_sql_unknown(StringValueCStr(sql)); if (result.status == 1) { VALUE title = rb_str_new2(result.title); VALUE statement = rb_str_new2(result.statement); free_lex_result(result); return rb_ary_new3(2, title, statement); } else { VALUE exception = rb_exc_new_cstr(rb_eRuntimeError, result.error); free_lex_result(result); rb_exc_raise(exception); return Qnil; } #endif #ifndef HAVE_LEX_SQL_UNKNOWN return Qnil; #endif } /* * * class AppDynamics::Instrumenter * */ static VALUE instrumenter_new(VALUE klass) { sky_instrumenter_t* instrumenter = malloc(sizeof(sky_instrumenter_t)); instrumenter->config = NULL; instrumenter->started = false; instrumenter->num_backends = 0; return Data_Wrap_Struct(klass, NULL, free, instrumenter); } static void* instrumenter_start_nogvl(sky_instrumenter_t* instrumenter) { int initRC; initRC = appd_sdk_init(instrumenter->config); if (initRC) { return (void*) false; } sky_activate_memprof(); instrumenter->started = true; return (void*) true; } static VALUE instrumenter_start_sdk(VALUE self) { sky_instrumenter_t* instrumenter; struct appd_config *config; VALUE rb_config; VALUE rb_config_hash; VALUE app_name; VALUE tier_name; VALUE node_name; VALUE controller_host; VALUE controller_port; VALUE controller_account; VALUE controller_access_key; VALUE controller_use_ssl; VALUE controller_cert_path; VALUE controller_http_proxy_host; VALUE controller_http_proxy_port; VALUE controller_http_proxy_username; VALUE controller_http_proxy_password; VALUE controller_http_proxy_password_file; VALUE controller_log_dir; VALUE controller_log_level; VALUE controller_log_max_num_files; VALUE controller_log_max_file_size; VALUE init_timeout_ms; VALUE logger; My_Struct(instrumenter, sky_instrumenter_t, no_instrumenter_msg); if (instrumenter->started) { rb_raise(rb_eRuntimeError, "%s", existing_instrumenter_msg); } // FIXME: Does the extra step of converting to a hash make sense here? rb_config = rb_iv_get(self, "@config"); rb_config_hash = rb_funcall(rb_config, rb_intern("to_native_hash"), 0); logger = rb_hash_aref(rb_config_hash, ID2SYM(rb_intern("logger"))); config = appd_config_init(); // FIXME: This could definitely be more elegant, maybe make new class to wrap appd_config // FIXME: Add Windows proxy settings app_name = rb_hash_aref(rb_config_hash, ID2SYM(rb_intern("app_name"))); CHECK_TYPE(app_name, T_STRING); LOG(logger, "info", "App Name: %s", StringValueCStr(app_name)); tier_name = rb_hash_aref(rb_config_hash, ID2SYM(rb_intern("tier_name"))); CHECK_TYPE(tier_name, T_STRING); LOG(logger, "info", "Tier Name: %s", StringValueCStr(tier_name)); node_name = rb_hash_aref(rb_config_hash, ID2SYM(rb_intern("node_name"))); CHECK_TYPE(node_name, T_STRING); LOG(logger, "info", "Node Name: %s", StringValueCStr(node_name)); controller_host = rb_hash_aref(rb_config_hash, ID2SYM(rb_intern("controller.host"))); CHECK_TYPE(controller_host, T_STRING); controller_port = rb_hash_aref(rb_config_hash, ID2SYM(rb_intern("controller.port"))); CHECK_TYPE(controller_port, T_FIXNUM); controller_account = rb_hash_aref(rb_config_hash, ID2SYM(rb_intern("controller.account"))); CHECK_TYPE(controller_account, T_STRING); controller_access_key = rb_hash_aref(rb_config_hash, ID2SYM(rb_intern("controller.access_key"))); CHECK_TYPE(controller_access_key, T_STRING); controller_use_ssl = rb_hash_aref(rb_config_hash, ID2SYM(rb_intern("controller.use_ssl"))); controller_cert_path = rb_hash_aref(rb_config_hash, ID2SYM(rb_intern("controller.cert_path"))); controller_http_proxy_host = rb_hash_aref(rb_config_hash, ID2SYM(rb_intern("controller.http_proxy_host"))); controller_http_proxy_port = rb_hash_aref(rb_config_hash, ID2SYM(rb_intern("controller.http_proxy_port"))); controller_http_proxy_username = rb_hash_aref(rb_config_hash, ID2SYM(rb_intern("controller.http_proxy_username"))); controller_http_proxy_password = rb_hash_aref(rb_config_hash, ID2SYM(rb_intern("controller.http_proxy_password"))); controller_http_proxy_password_file = rb_hash_aref(rb_config_hash, ID2SYM(rb_intern("controller.http_proxy_password_file"))); controller_log_dir = rb_hash_aref(rb_config_hash, ID2SYM(rb_intern("controller.log_dir"))); controller_log_level = rb_hash_aref(rb_config_hash, ID2SYM(rb_intern("controller.log_level"))); controller_log_max_num_files = rb_hash_aref(rb_config_hash, ID2SYM(rb_intern("controller.log_max_num_files"))); controller_log_max_file_size = rb_hash_aref(rb_config_hash, ID2SYM(rb_intern("controller.log_max_file_size"))); init_timeout_ms = rb_hash_aref(rb_config_hash, ID2SYM(rb_intern("init_timeout_ms"))); CHECK_TYPE(init_timeout_ms, T_FIXNUM); appd_config_set_app_name(config, StringValueCStr(app_name)); appd_config_set_tier_name(config, StringValueCStr(tier_name)); appd_config_set_node_name(config, StringValueCStr(node_name)); appd_config_set_controller_host(config, StringValueCStr(controller_host)); appd_config_set_controller_port(config, FIX2UINT(controller_port)); appd_config_set_controller_account(config, StringValueCStr(controller_account)); appd_config_set_controller_access_key(config, StringValueCStr(controller_access_key)); appd_config_set_controller_use_ssl(config, RTEST(controller_use_ssl) ? 1 : 0); appd_config_set_logging_log_dir(config, StringValueCStr(controller_log_dir)); if (controller_log_level != Qnil) { appd_config_set_logging_min_level(config, FIX2UINT(controller_log_level)); } if (controller_log_max_num_files != Qnil) { appd_config_set_logging_max_num_files(config, FIX2UINT(controller_log_max_num_files)); } if (controller_log_max_file_size != Qnil) { appd_config_set_logging_max_file_size_bytes(config, FIX2UINT(controller_log_max_file_size)); } appd_config_set_init_timeout_ms(config, FIX2UINT(init_timeout_ms)); if (controller_cert_path != Qnil) { if (TYPE(controller_cert_path) == T_STRING) { LOG(logger, "info", "Setting cert path: %s", StringValueCStr(controller_cert_path)); appd_config_set_controller_certificate_file(config, StringValueCStr(controller_cert_path)); } else { LOG(logger, "warn", "Ignoring cert path, invalid type"); } } if (controller_http_proxy_host != Qnil) { if (TYPE(controller_http_proxy_host) == T_STRING) { LOG(logger, "info", "Setting HTTP proxy host: %s", StringValueCStr(controller_http_proxy_host)); appd_config_set_controller_http_proxy_host(config, StringValueCStr(controller_http_proxy_host)); } else { LOG(logger, "warn", "Ignoring HTTP proxy host, invalid type"); } } if (controller_http_proxy_port != Qnil) { if (TYPE(controller_http_proxy_port) == T_FIXNUM) { LOG(logger, "info", "Setting HTTP proxy port: %i", FIX2UINT(controller_http_proxy_port)); appd_config_set_controller_http_proxy_port(config, FIX2UINT(controller_http_proxy_port)); } else { LOG(logger, "warn", "Ignoring HTTP proxy port, invalid type"); } } if (controller_http_proxy_username != Qnil) { if (TYPE(controller_http_proxy_username) == T_STRING) { LOG(logger, "info", "Setting HTTP proxy username: %s", StringValueCStr(controller_http_proxy_username)); appd_config_set_controller_http_proxy_username(config, StringValueCStr(controller_http_proxy_username)); } else { LOG(logger, "warn", "Ignoring HTTP proxy username, invalid type"); } } if (controller_http_proxy_password != Qnil) { if (TYPE(controller_http_proxy_password) == T_STRING) { LOG(logger, "info", "Setting HTTP proxy password"); appd_config_set_controller_http_proxy_password(config, StringValueCStr(controller_http_proxy_password)); } else { LOG(logger, "warn", "Ignoring HTTP proxy password, invalid type"); } } if (controller_http_proxy_password_file != Qnil) { if (TYPE(controller_http_proxy_password_file) == T_STRING) { LOG(logger, "info", "Setting HTTP proxy password file: %s", StringValueCStr(controller_http_proxy_password_file)); appd_config_set_controller_http_proxy_password_file(config, StringValueCStr(controller_http_proxy_password_file)); } else { LOG(logger, "warn", "Ignoring HTTP proxy password file, invalid type"); } } instrumenter->config = config; if (WITHOUT_GVL(instrumenter_start_nogvl, instrumenter)) { INFO("Initialized AppDynamics SDK"); instrumenter->started = true; return Qtrue; } else { ERROR("Unable to initialize AppDynamics SDK"); return Qfalse; } } // TODO: Revist this since it doesn't do much static VALUE instrumenter_start(VALUE self) { sky_instrumenter_t* instrumenter; My_Struct(instrumenter, sky_instrumenter_t, no_instrumenter_msg); if (instrumenter->started) { rb_raise(rb_eRuntimeError, "%s", existing_instrumenter_msg); } return Qtrue; } static VALUE instrumenter_stop(VALUE self) { sky_instrumenter_t* instrumenter; My_Struct(instrumenter, sky_instrumenter_t, no_instrumenter_msg); if (instrumenter->started) { INFO("Terminating AppDynamics SDK"); sky_deactivate_memprof(); TRACE("appd_sdk_term before"); appd_sdk_term(); instrumenter->started = false; DEBUG("Terminated AppDynamics SDK"); } return Qnil; } int sort_span_duration_desc(const void *a, const void *b) { sky_span_t *span_a = *(sky_span_t **)a; sky_span_t *span_b = *(sky_span_t **)b; long long a_dur = span_a->stop - span_a->start; long long b_dur = span_b->stop - span_b->start; return (int)(b_dur - a_dur); } static VALUE instrumenter_submit_trace(VALUE self, VALUE rb_trace) { sky_trace_t* trace; uint i = 0; Transfer_Struct(trace, rb_trace, sky_trace_t, no_trace_msg); if (sky_have_memprof()) { trace->allocations = sky_consume_allocations(); } TRACE("start: %llu", trace->start); TRACE("endpoint: %s", trace->endpoint); if (sky_have_memprof()) { TRACE("allocations: %" PRIu64, trace->allocations); } TRACE("num_spans: %d", trace->num_spans); TRACE("spans:"); // TODO: Only iterate here if tracing for (i=0; i < trace->num_spans; i++) { sky_span_t *span = trace->spans[i]; TRACE(" %u:", i); TRACE(" is_exitcall: %i", span->is_exitcall); TRACE(" start: %llu", span->start); TRACE(" stop: %llu", span->stop); TRACE(" category: %s", span->category); TRACE(" title: %s", span->title ? span->title : "-"); TRACE(" description: %s", span->description ? span->description : "-"); } if (appd_bt_is_snapshotting(trace->handle)) { sky_span_t *gc_span = NULL; sky_span_t **sql_spans = malloc(trace->num_spans * sizeof(sky_span_t*)); uint num_sql_spans = 0; TRACE("Logging additional snapshot details"); // Set starting URL if (trace->request_url != NULL) { LOG(rb_mAppDynamics, "log_debug", "Setting URL: %s", trace->request_url); appd_bt_set_url(trace->handle, trace->request_url); } if (sky_have_memprof()) { char buffer[20]; // 64-bit number is 19 chars max sprintf(buffer, "%" PRIu64, trace->allocations); appd_bt_add_user_data(trace->handle, "Objects Allocated", buffer); } // Find GC and SQL spans for (i=0; i < trace->num_spans; i++) { sky_span_t *span = trace->spans[i]; if (strcmp(span->category, "db.sql.query") == 0) { sql_spans[num_sql_spans++] = span; } else if (strcmp(span->category, "noise.gc") == 0) { gc_span = span; } } if (gc_span != NULL) { // Log GC details in snapshot char value[25]; // More than enough for duration float duration; duration = (float)(gc_span->stop - gc_span->start) / 10; sprintf(value, "%.2f ms", duration); TRACE("GC Time: %s", value); appd_bt_add_user_data(trace->handle, "GC Time", value); } // Sort SQL spans in reverse duration order qsort(sql_spans, num_sql_spans, sizeof(sky_span_t*), sort_span_duration_desc); // Log details about each SQL statement in snapshot for (i=0; i < num_sql_spans && i < 10; i++) { char key[25]; // Allows for 18 string chars and up to 7 numbers, more than enough char *value = NULL; float duration; int aspfRC; sky_span_t *span = sql_spans[i]; sprintf(key, "Top SQL statement %i", i+1); duration = (float)(span->stop - span->start) / 10; aspfRC = asprintf(&value, "%.2f ms: %s", duration, span->description); if (aspfRC > -1) { TRACE("%s: %s", key, value); appd_bt_add_user_data(trace->handle, key, value); } else { ERROR("asprintf failed; unable to log slow SQL statement"); } free(value); } free(sql_spans); } else { TRACE("Not snapshotting, won't log extra details"); } TRACE("Ending BT; endpoint=%s; handle=%p", trace->endpoint, trace->handle); appd_bt_end(trace->handle); return Qnil; } static VALUE instrumenter_track_desc(VALUE self, VALUE rb_endpoint, VALUE rb_desc) { UNUSED(self); UNUSED(rb_endpoint); UNUSED(rb_desc); return Qtrue; } /* * * class AppDynamics::Trace * */ void trace_mark(sky_trace_t *trace) { uint i; if (trace->spans != NULL) { for (i = 0; i < trace->num_spans; i++) { sky_span_t *span = trace->spans[i]; // This is a Ruby object, so mark it to show that we care about it rb_gc_mark(span->meta); } } } void trace_free(sky_trace_t *trace) { uint i; free(trace->endpoint); free(trace->request_url); for (i = 0; i < trace->num_spans; i++) { sky_span_t *span = trace->spans[i]; free(span->category); free(span->title); free(span->description); // No need to free meta since it is a Ruby object, but we do need to mark it free(span); } free(trace->spans); free(trace); } static VALUE trace_new(VALUE klass, VALUE start, VALUE uuid, VALUE endpoint, VALUE meta) { sky_trace_t *trace = malloc(sizeof(sky_trace_t)); char *uuid_str = NULL; char *endpoint_str = NULL; char *correlation_header = NULL; char *request_url = NULL; char *tmp = NULL; bool ignored; UNUSED(klass); CHECK_NUMERIC(start); CHECK_TYPE(endpoint, T_STRING); if (meta != Qnil) { VALUE rb_correlation_header; VALUE rb_request_url; CHECK_TYPE(meta, T_HASH); // Could/should the uuid be used for the correlation header? rb_correlation_header = rb_hash_aref(meta, ID2SYM(rb_intern("correlation_header"))); if (TYPE(rb_correlation_header) == T_STRING) { correlation_header = StringValueCStr(rb_correlation_header); } rb_request_url = rb_hash_aref(meta, ID2SYM(rb_intern("request_url"))); if (TYPE(rb_request_url) == T_STRING) { tmp = StringValueCStr(rb_request_url); request_url = malloc(sizeof(char) * strlen(tmp) + 1); strcpy(request_url, tmp); } } uuid_str = StringValueCStr(uuid); endpoint_str = StringValueCStr(endpoint); ignored = (strcmp(endpoint_str, IGNORE) == 0); LOG(rb_mAppDynamics, "log_debug", "trace=%s; BT Begin: %s, %s, ignored=%d", uuid_str, endpoint_str, correlation_header, ignored); if (ignored) { trace->handle = NULL; trace->ignored = true; } else { trace->handle = appd_bt_begin(endpoint_str, correlation_header); trace->ignored = false; LOG(rb_mAppDynamics, "log_trace", "trace=%s; BT handle=%p", uuid_str, trace->handle); if (trace->handle == NULL) { LOG(rb_mAppDynamics, "log_warn", "trace=%s; Unable to obtain a BT handle, request will not be tracked.", uuid_str); } } trace->start = NUM2ULL(start); tmp = StringValueCStr(endpoint); trace->endpoint = malloc(sizeof(char) * strlen(tmp) + 1); strcpy(trace->endpoint, tmp); trace->request_url = request_url; // Reset allocations counter if (sky_have_memprof()) { sky_consume_allocations(); } trace->num_spans = 0; trace->spans = NULL; return Data_Wrap_Struct(klass, trace_mark, trace_free, trace); } static VALUE trace_get_started_at(VALUE self) { sky_trace_t* trace; My_Struct(trace, sky_trace_t, no_trace_msg); return ULL2NUM(trace->start); } static VALUE trace_get_endpoint(VALUE self) { sky_trace_t* trace; My_Struct(trace, sky_trace_t, no_trace_msg); return rb_str_new_cstr(trace->endpoint); } // AppDynamics doesn't allow us to set after the fact static VALUE trace_set_endpoint(VALUE self, VALUE endpoint) { return Qnil; } static VALUE trace_set_exception(VALUE self, VALUE exception) { sky_trace_t* trace; VALUE exception_str; char* error_name; bool mark_bt_as_error; My_Struct(trace, sky_trace_t, no_trace_msg); if (trace->ignored) { return Qnil; } exception_str = rb_funcall(exception, rb_intern("to_s"), 0); error_name = StringValueCStr(exception_str); mark_bt_as_error = true; DEBUG("Adding error: %s", error_name); appd_bt_add_error(trace->handle, APPD_LEVEL_ERROR, error_name, mark_bt_as_error); return Qnil; } static VALUE trace_get_uuid(VALUE self) { return Qnil; } static VALUE trace_start_span(VALUE self, VALUE time, VALUE category) { sky_trace_t* trace; sky_span_t* span = malloc(sizeof(sky_span_t)); uint span_id; char* tmp; My_Struct(trace, sky_trace_t, no_trace_msg); if (trace->ignored) { return Qnil; } CHECK_NUMERIC(time); CHECK_TYPE(category, T_STRING); span->is_exitcall = false; span->start = NUM2ULL(time); tmp = StringValueCStr(category); span->category = malloc(sizeof(char) * strlen(tmp) + 1); strcpy(span->category, tmp); span->title = NULL; span->description = NULL; span->meta = Qnil; span_id = trace->num_spans; if (span_id % SPAN_BATCH == 0) { if (span_id >= MAX_SPANS) { rb_raise(rb_eRuntimeError, "Exceeded maximum number of spans (%d)", MAX_SPANS); } TRACE("Growing spans size to %i", span_id+SPAN_BATCH); trace->spans = realloc(trace->spans, (span_id+SPAN_BATCH) * sizeof(sky_span_t*)); } trace->spans[span_id] = span; trace->num_spans++; return UINT2NUM(span_id); } static VALUE trace_stop_span(VALUE self, VALUE span_id, VALUE time) { sky_trace_t* trace; sky_span_t* span; UNUSED(self); My_Struct(trace, sky_trace_t, no_trace_msg); if (trace->ignored) { return Qnil; } CHECK_TYPE(span_id, T_FIXNUM); CHECK_NUMERIC(time); // In theory we should check that the spans are closed in order span = trace->spans[FIX2UINT(span_id)]; span->stop = NUM2ULL(time); if (span->is_exitcall) { char *title; int rc; title = span->title ? span->title : span->category; DEBUG("Setting exitcall title: %s", title); rc = appd_exitcall_set_details(span->handle, title); if (rc) { ERROR("Failed to set exitcall details; %s; err=%i", title, rc); } appd_exitcall_end(span->handle); } return Qnil; } static VALUE trace_span_get_category(VALUE self, VALUE span_id) { sky_trace_t* trace; sky_span_t* span; My_Struct(trace, sky_trace_t, no_trace_msg); if (trace->ignored) { return Qnil; } CHECK_TYPE(span_id, T_FIXNUM); span = trace->spans[FIX2UINT(span_id)]; return rb_str_new_cstr(span->category); } static VALUE trace_span_set_title(VALUE self, VALUE span_id, VALUE title) { sky_trace_t* trace; sky_span_t* span; char *tmp; My_Struct(trace, sky_trace_t, no_trace_msg); if (trace->ignored) { return Qnil; } CHECK_TYPE(span_id, T_FIXNUM); CHECK_TYPE(title, T_STRING); span = trace->spans[FIX2UINT(span_id)]; tmp = StringValueCStr(title); span->title = malloc(sizeof(char) * strlen(tmp) + 1); strcpy(span->title, tmp); return Qnil; } static VALUE trace_span_get_title(VALUE self, VALUE span_id) { sky_trace_t* trace; sky_span_t* span; My_Struct(trace, sky_trace_t, no_trace_msg); if (trace->ignored) { return Qnil; } CHECK_TYPE(span_id, T_FIXNUM); span = trace->spans[FIX2UINT(span_id)]; return span->title ? rb_str_new_cstr(span->title) : Qnil; } static VALUE trace_span_set_description(VALUE self, VALUE span_id, VALUE desc) { sky_trace_t* trace; sky_span_t* span; char *tmp; My_Struct(trace, sky_trace_t, no_trace_msg); if (trace->ignored) { return Qnil; } CHECK_TYPE(span_id, T_FIXNUM); CHECK_TYPE(desc, T_STRING); span = trace->spans[FIX2UINT(span_id)]; tmp = StringValueCStr(desc); span->description = malloc(sizeof(char) * strlen(tmp) + 1); strcpy(span->description, tmp); return Qnil; } static VALUE trace_span_set_meta(VALUE self, VALUE span_id, VALUE meta) { sky_trace_t* trace; sky_span_t* span; My_Struct(trace, sky_trace_t, no_trace_msg); if (trace->ignored) { return Qnil; } CHECK_TYPE(span_id, T_FIXNUM); CHECK_TYPE(meta, T_HASH); span = trace->spans[FIX2UINT(span_id)]; span->meta = meta; return Qnil; } static VALUE trace_span_started(VALUE self, VALUE span_id) { sky_trace_t* trace; sky_span_t* span; VALUE rb_mAppDynamics; VALUE rb_mBackend; VALUE rb_iBackendSub; My_Struct(trace, sky_trace_t, no_trace_msg); if (trace->ignored) { return Qnil; } CHECK_TYPE(span_id, T_FIXNUM); span = trace->spans[FIX2UINT(span_id)]; rb_mAppDynamics = rb_define_module("AppDynamics"); rb_mBackend = rb_define_module_under(rb_mAppDynamics, "Backend"); rb_iBackendSub = rb_funcall(rb_mBackend, rb_intern("build"), 4, rb_str_new_cstr(span->category), span->title ? rb_str_new_cstr(span->title) : Qnil, span->description ? rb_str_new_cstr(span->description) : Qnil, span->meta); if (rb_iBackendSub != Qnil) { VALUE rb_oInstrumenter; sky_instrumenter_t* instrumenter; VALUE rb_backend_name; char* backend_name; bool has_backend; uint i; int rc; DEBUG("Attempting to treat as an exit call"); // TODO: We can streamline this by maintining references in the C-code rb_oInstrumenter = rb_iv_get(self, "@instrumenter"); Get_Struct(instrumenter, rb_oInstrumenter, sky_instrumenter_t, "missing native instrumenter"); // FIXME: Maybe this should include the type too rb_backend_name = rb_funcall(rb_iBackendSub, rb_intern("backend_name"), 0); backend_name = StringValueCStr(rb_backend_name); has_backend = false; for (i = 0; i < instrumenter->num_backends; i++) { if (strcmp(instrumenter->backends[i], backend_name) == 0) { has_backend = true; break; } } if (has_backend) { TRACE("Backend already added: %s", backend_name); } else { int backend_idx; VALUE backend_type; VALUE identifying_properties; uint ipc; DEBUG("Adding backend: %s", backend_name); backend_type = rb_funcall(rb_iBackendSub, rb_intern("backend_type"), 0); appd_backend_declare(StringValueCStr(backend_type), backend_name); identifying_properties = rb_funcall(rb_iBackendSub, rb_intern("identifying_properties_array"), 0); ipc = (int) RARRAY_LEN(identifying_properties); for (i = 0; i < ipc; i += 2) { VALUE rb_key = rb_ary_entry(identifying_properties, i); VALUE rb_val = rb_ary_entry(identifying_properties, i+1); char *key = StringValueCStr(rb_key); char *val = StringValueCStr(rb_val); DEBUG("Identifying: %s - %s\n", key, val); rc = appd_backend_set_identifying_property(backend_name, key, val); if (rc) { ERROR("Unable to set identifying property for backend: %s; key=%s; val=%s; err=%d.", backend_name, key, val, rc); } } backend_idx = instrumenter->num_backends++; strcpy(instrumenter->backends[backend_idx], backend_name); } rc = appd_backend_add(backend_name); if (rc) { ERROR("Unable to add backend: %s; err=%d.", backend_name, rc); } TRACE("Starting exit call; endpoint=%s; backend=%s", trace->endpoint, backend_name); span->is_exitcall = true; span->handle = appd_exitcall_begin(trace->handle, backend_name); TRACE("exit call handle=%p", span->handle); } return Qnil; } static VALUE trace_span_set_exception(VALUE self, VALUE span_id, VALUE exception, VALUE exception_details) { sky_trace_t* trace; sky_span_t* span; VALUE exception_str; char* error_name; bool mark_bt_as_error; My_Struct(trace, sky_trace_t, no_trace_msg); if (trace->ignored) { return Qnil; } span = trace->spans[FIX2UINT(span_id)]; if (span->is_exitcall) { if (exception != Qnil) { exception_str = rb_funcall(exception, rb_intern("to_s"), 0); } else { exception_str = rb_funcall(exception_details, rb_intern("join"), rb_str_new_cstr(", ")); } error_name = StringValueCStr(exception_str); // We'll report the bt error separately mark_bt_as_error = false; DEBUG("Adding span error: %s\n", error_name); appd_exitcall_add_error(span->handle, APPD_LEVEL_ERROR, error_name, mark_bt_as_error); } return Qnil; } static VALUE trace_span_get_correlation_header(VALUE self, VALUE span_id) { sky_trace_t* trace; sky_span_t* span; My_Struct(trace, sky_trace_t, no_trace_msg); if (trace->ignored) { return Qnil; } span = trace->spans[FIX2UINT(span_id)]; if (span->is_exitcall) { return rb_str_new_cstr(appd_exitcall_get_correlation_header(span->handle)); } else { return Qnil; } } static VALUE trace_is_snapshotting(VALUE self) { sky_trace_t* trace; My_Struct(trace, sky_trace_t, no_trace_msg); if (trace->ignored) { return Qfalse; } if (appd_bt_is_snapshotting(trace->handle)) { return Qtrue; } else { return Qtrue; } } static VALUE trace_add_snapshot_details(VALUE self, VALUE key, VALUE value) { sky_trace_t* trace; char* c_key; char* c_value; My_Struct(trace, sky_trace_t, no_trace_msg); if (trace->ignored) { return Qnil; } c_key = StringValueCStr(key); c_value = StringValueCStr(value); appd_bt_add_user_data(trace->handle, c_key, c_value); return Qnil; } static VALUE trace_add_snapshot_url(VALUE self, VALUE url) { sky_trace_t* trace; char* c_url; My_Struct(trace, sky_trace_t, no_trace_msg); if (trace->ignored) { return Qnil; } c_url = StringValueCStr(url); appd_bt_set_url(trace->handle, c_url); return Qnil; } // FIXME: Remove duplication in next two methods static VALUE metrics_define(VALUE self, VALUE name) { char* metric_name; int rc = asprintf(&metric_name, "Custom Metrics|Ruby|%s", StringValueCStr(name)); if (rc > -1) { TRACE("Registering metric: %s", metric_name) appd_custom_metric_add(NULL, metric_name, APPD_TIMEROLLUP_TYPE_AVERAGE, APPD_CLUSTERROLLUP_TYPE_INDIVIDUAL, APPD_HOLEHANDLING_TYPE_RATE_COUNTER); } else { ERROR("Unable to allocate metric_name"); } free(metric_name); return Qnil; } static VALUE metrics_report(VALUE self, VALUE name, VALUE value) { char* metric_name; int rc = asprintf(&metric_name, "Custom Metrics|Ruby|%s", StringValueCStr(name)); if (rc > -1) { unsigned long c_value = NUM2ULONG(value); TRACE("Reporting metric: %s: %lu", metric_name, c_value); appd_custom_metric_report(NULL, metric_name, c_value); } else { ERROR("Unable to allocate metric_name"); } free(metric_name); return Qnil; } #endif void Init_app_dynamics_native() { VALUE rb_cTrace; VALUE rb_cInstrumenter; VALUE rb_cBackgroundMetrics; rb_mAppDynamics = rb_define_module("AppDynamics"); // AppDynamics custom methods rb_define_singleton_method(rb_mAppDynamics, "native?", check_native, 0); rb_define_singleton_method(rb_mAppDynamics, "native_correlation_header", get_correlation_header_name, 0); rb_define_singleton_method(rb_mAppDynamics, "lex_sql", lex_sql, 1); rb_cTrace = rb_const_get(rb_mAppDynamics, rb_intern("Trace")); // Skylight required methods rb_define_singleton_method(rb_cTrace, "native_new", trace_new, 4); rb_define_method(rb_cTrace, "native_get_started_at", trace_get_started_at, 0); rb_define_method(rb_cTrace, "native_get_endpoint", trace_get_endpoint, 0); rb_define_method(rb_cTrace, "native_set_endpoint", trace_set_endpoint, 1); rb_define_method(rb_cTrace, "native_set_exception", trace_set_exception, 1); rb_define_method(rb_cTrace, "native_get_uuid", trace_get_uuid, 0); rb_define_method(rb_cTrace, "native_start_span", trace_start_span, 2); rb_define_method(rb_cTrace, "native_stop_span", trace_stop_span, 2); rb_define_method(rb_cTrace, "native_span_get_category", trace_span_get_category, 1); rb_define_method(rb_cTrace, "native_span_set_title", trace_span_set_title, 2); rb_define_method(rb_cTrace, "native_span_get_title", trace_span_get_title, 1); rb_define_method(rb_cTrace, "native_span_set_description", trace_span_set_description, 2); rb_define_method(rb_cTrace, "native_span_set_meta", trace_span_set_meta, 2); rb_define_method(rb_cTrace, "native_span_started", trace_span_started, 1); rb_define_method(rb_cTrace, "native_span_set_exception", trace_span_set_exception, 3); rb_define_method(rb_cTrace, "native_span_get_correlation_header", trace_span_get_correlation_header, 1); // AppDynamics custom methods rb_define_method(rb_cTrace, "native_is_snapshotting", trace_is_snapshotting, 0); rb_define_method(rb_cTrace, "native_add_snapshot_details", trace_add_snapshot_details, 2); rb_define_method(rb_cTrace, "native_set_snapshot_url", trace_add_snapshot_url, 1); rb_cInstrumenter = rb_const_get(rb_mAppDynamics, rb_intern("Instrumenter")); // Skylight required methods rb_define_singleton_method(rb_cInstrumenter, "native_new", instrumenter_new, 0); rb_define_method(rb_cInstrumenter, "native_start", instrumenter_start, 0); rb_define_method(rb_cInstrumenter, "native_start_sdk", instrumenter_start_sdk, 0); rb_define_method(rb_cInstrumenter, "native_stop", instrumenter_stop, 0); rb_define_method(rb_cInstrumenter, "native_submit_trace", instrumenter_submit_trace, 1); rb_define_method(rb_cInstrumenter, "native_track_desc", instrumenter_track_desc, 2); // AppDynamics custom methods rb_cBackgroundMetrics = rb_const_get(rb_mAppDynamics, rb_intern("BackgroundMetrics")); rb_define_method(rb_cBackgroundMetrics, "native_define_metric", metrics_define, 1); rb_define_method(rb_cBackgroundMetrics, "native_report_metric", metrics_report, 2); }
native_correlation_header()
click to toggle source
static VALUE get_correlation_header_name(VALUE klass) { return Qnil; } static VALUE lex_sql(VALUE klass) { return Qnil; } static VALUE instrumenter_new(VALUE klass, VALUE rb_config) { return Qnil; } static VALUE instrumenter_start(VALUE self) { return Qnil; } static VALUE instrumenter_start_sdk(VALUE self) { return Qnil; } static VALUE instrumenter_stop(VALUE self) { return Qnil; } static VALUE instrumenter_submit_trace(VALUE self, VALUE rb_trace) { return Qnil; } static VALUE instrumenter_track_desc(VALUE self, VALUE rb_endpoint, VALUE rb_desc) { return Qnil; } static VALUE trace_new(VALUE klass, VALUE start, VALUE uuid, VALUE endpoint, VALUE meta) { return Qnil; } static VALUE trace_get_started_at(VALUE self) { return Qnil; } static VALUE trace_get_endpoint(VALUE self) { return Qnil; } static VALUE trace_set_endpoint(VALUE self, VALUE endpoint) { return Qnil; } static VALUE trace_set_exception(VALUE self, VALUE exception) { return Qnil; } static VALUE trace_get_uuid(VALUE self) { return Qnil; } static VALUE trace_start_span(VALUE self, VALUE time, VALUE category) { return Qnil; } static VALUE trace_stop_span(VALUE self, VALUE span_id, VALUE time) { return Qnil; } static VALUE trace_span_get_category(VALUE self, VALUE span_id) { return Qnil; } static VALUE trace_span_set_title(VALUE self, VALUE span_id, VALUE title) { return Qnil; } static VALUE trace_span_get_title(VALUE self, VALUE span_id) { return Qnil; } static VALUE trace_span_set_description(VALUE self, VALUE span_id, VALUE desc) { return Qnil; } static VALUE trace_span_set_meta(VALUE self, VALUE span_id, VALUE meta) { return Qnil; } static VALUE trace_span_started(VALUE self, VALUE span_id) { return Qnil; } static VALUE trace_span_set_exception(VALUE self, VALUE span_id, VALUE exception, VALUE exception_details) { return Qnil; } static VALUE trace_span_get_correlation_header(VALUE self, VALUE span_id) { return Qnil; } static VALUE trace_is_snapshotting(VALUE self) { return Qnil; } static VALUE trace_add_snapshot_details(VALUE self, VALUE key, VALUE value) { return Qnil; } static VALUE trace_add_snapshot_url(VALUE self, VALUE url) { return Qnil; } static VALUE metrics_define(VALUE self, VALUE name) { return Qnil; } static VALUE metrics_report(VALUE self, VALUE name, VALUE value) { return Qnil; } #endif #ifdef HAVE_APPDYNAMICS_H #include <time.h> #include <stdio.h> #include <inttypes.h> #define TO_S(VAL) \ RSTRING_PTR(rb_funcall(VAL, rb_intern("to_s"), 0)) #define CHECK_TYPE(VAL, T) \ do { \ if (TYPE(VAL) != T) { \ rb_raise(rb_eArgError, "expected " #VAL " to be " #T " but was '%s' (%s [%i])", \ TO_S(VAL), rb_obj_classname(VAL), TYPE(VAL)); \ return Qnil; \ } \ } while(0) #define CHECK_NUMERIC(VAL) \ do { \ if (TYPE(VAL) != T_BIGNUM && \ TYPE(VAL) != T_FIXNUM) { \ rb_raise(rb_eArgError, "expected " #VAL " to be numeric but was '%s' (%s [%i])", \ TO_S(VAL), rb_obj_classname(VAL), TYPE(VAL)); \ return Qnil; \ } \ } while(0) \ #define My_Struct(name, Type, msg) \ Get_Struct(name, self, Type, msg); \ #define Transfer_My_Struct(name, Type, msg) \ My_Struct(name, Type, msg); \ DATA_PTR(self) = NULL; \ #define Transfer_Struct(name, obj, Type, msg) \ Get_Struct(name, obj, Type, msg); \ DATA_PTR(obj) = NULL; \ #define Get_Struct(name, obj, Type, msg) \ Data_Get_Struct(obj, Type, name); \ if (name == NULL) { \ rb_raise(rb_eRuntimeError, "%s", msg); \ } /** * Ruby GVL helpers */ // FIXME: This conditional doesn't logically cover every case #if defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL) && \ defined(HAVE_RUBY_THREAD_H) // Ruby 2.0+ #include <ruby/thread.h> typedef void* (*blocking_fn_t)(void*); #define WITHOUT_GVL(fn, a) \ rb_thread_call_without_gvl((blocking_fn_t)(fn), (a), 0, 0) // Ruby 1.9 #elif defined(HAVE_RB_THREAD_BLOCKING_REGION) typedef VALUE (*blocking_fn_t)(void*); #define WITHOUT_GVL(fn, a) \ rb_thread_blocking_region((blocking_fn_t)(fn), (a), 0, 0) #endif /** * Ruby types defined here */ static const char* existing_instrumenter_msg = "Instrumenter is already running"; static const char* no_instrumenter_msg = "Instrumenter not currently running"; static const char* no_trace_msg = "Trace is not available"; /* * Logging */ #define LOG(target, method, fmt, ...) \ do { \ char* msg; \ int assigned = asprintf(&msg, fmt, ##__VA_ARGS__); \ if (assigned > -1) { \ rb_funcall(target, rb_intern(method), 1, rb_str_new_cstr(msg)); \ } else { \ rb_raise(rb_eRuntimeError, "Unable to log from native"); \ } \ free(msg); \ } while (0) #define TRACE(fmt, ...) \ LOG(self, "log_trace", fmt, ##__VA_ARGS__); #define DEBUG(fmt, ...) \ LOG(self, "log_debug", fmt, ##__VA_ARGS__); #define INFO(fmt, ...) \ LOG(self, "log_info", fmt, ##__VA_ARGS__); #define WARN(fmt, ...) \ LOG(self, "log_warn", fmt, ##__VA_ARGS__); #define ERROR(fmt, ...) \ LOG(self, "log_error", fmt, ##__VA_ARGS__); // FIXME: // - Add more type checking // - Split up methods /* * Returns true if the native SDK is present. */ static VALUE check_native(VALUE klass) { UNUSED(klass); return Qtrue; } /* * AppDynamics correlation header name * @return [String] */ static VALUE get_correlation_header_name(VALUE klass) { UNUSED(klass); return rb_str_new_cstr(APPD_CORRELATION_HEADER_NAME); } /* * Lex SQL queries * @return [String, String] */ static VALUE lex_sql(VALUE klass, VALUE sql) { #ifdef HAVE_LEX_SQL_UNKNOWN sql_lex_result_t result; UNUSED(klass); CHECK_TYPE(sql, T_STRING); result = lex_sql_unknown(StringValueCStr(sql)); if (result.status == 1) { VALUE title = rb_str_new2(result.title); VALUE statement = rb_str_new2(result.statement); free_lex_result(result); return rb_ary_new3(2, title, statement); } else { VALUE exception = rb_exc_new_cstr(rb_eRuntimeError, result.error); free_lex_result(result); rb_exc_raise(exception); return Qnil; } #endif #ifndef HAVE_LEX_SQL_UNKNOWN return Qnil; #endif } /* * * class AppDynamics::Instrumenter * */ static VALUE instrumenter_new(VALUE klass) { sky_instrumenter_t* instrumenter = malloc(sizeof(sky_instrumenter_t)); instrumenter->config = NULL; instrumenter->started = false; instrumenter->num_backends = 0; return Data_Wrap_Struct(klass, NULL, free, instrumenter); } static void* instrumenter_start_nogvl(sky_instrumenter_t* instrumenter) { int initRC; initRC = appd_sdk_init(instrumenter->config); if (initRC) { return (void*) false; } sky_activate_memprof(); instrumenter->started = true; return (void*) true; } static VALUE instrumenter_start_sdk(VALUE self) { sky_instrumenter_t* instrumenter; struct appd_config *config; VALUE rb_config; VALUE rb_config_hash; VALUE app_name; VALUE tier_name; VALUE node_name; VALUE controller_host; VALUE controller_port; VALUE controller_account; VALUE controller_access_key; VALUE controller_use_ssl; VALUE controller_cert_path; VALUE controller_http_proxy_host; VALUE controller_http_proxy_port; VALUE controller_http_proxy_username; VALUE controller_http_proxy_password; VALUE controller_http_proxy_password_file; VALUE controller_log_dir; VALUE controller_log_level; VALUE controller_log_max_num_files; VALUE controller_log_max_file_size; VALUE init_timeout_ms; VALUE logger; My_Struct(instrumenter, sky_instrumenter_t, no_instrumenter_msg); if (instrumenter->started) { rb_raise(rb_eRuntimeError, "%s", existing_instrumenter_msg); } // FIXME: Does the extra step of converting to a hash make sense here? rb_config = rb_iv_get(self, "@config"); rb_config_hash = rb_funcall(rb_config, rb_intern("to_native_hash"), 0); logger = rb_hash_aref(rb_config_hash, ID2SYM(rb_intern("logger"))); config = appd_config_init(); // FIXME: This could definitely be more elegant, maybe make new class to wrap appd_config // FIXME: Add Windows proxy settings app_name = rb_hash_aref(rb_config_hash, ID2SYM(rb_intern("app_name"))); CHECK_TYPE(app_name, T_STRING); LOG(logger, "info", "App Name: %s", StringValueCStr(app_name)); tier_name = rb_hash_aref(rb_config_hash, ID2SYM(rb_intern("tier_name"))); CHECK_TYPE(tier_name, T_STRING); LOG(logger, "info", "Tier Name: %s", StringValueCStr(tier_name)); node_name = rb_hash_aref(rb_config_hash, ID2SYM(rb_intern("node_name"))); CHECK_TYPE(node_name, T_STRING); LOG(logger, "info", "Node Name: %s", StringValueCStr(node_name)); controller_host = rb_hash_aref(rb_config_hash, ID2SYM(rb_intern("controller.host"))); CHECK_TYPE(controller_host, T_STRING); controller_port = rb_hash_aref(rb_config_hash, ID2SYM(rb_intern("controller.port"))); CHECK_TYPE(controller_port, T_FIXNUM); controller_account = rb_hash_aref(rb_config_hash, ID2SYM(rb_intern("controller.account"))); CHECK_TYPE(controller_account, T_STRING); controller_access_key = rb_hash_aref(rb_config_hash, ID2SYM(rb_intern("controller.access_key"))); CHECK_TYPE(controller_access_key, T_STRING); controller_use_ssl = rb_hash_aref(rb_config_hash, ID2SYM(rb_intern("controller.use_ssl"))); controller_cert_path = rb_hash_aref(rb_config_hash, ID2SYM(rb_intern("controller.cert_path"))); controller_http_proxy_host = rb_hash_aref(rb_config_hash, ID2SYM(rb_intern("controller.http_proxy_host"))); controller_http_proxy_port = rb_hash_aref(rb_config_hash, ID2SYM(rb_intern("controller.http_proxy_port"))); controller_http_proxy_username = rb_hash_aref(rb_config_hash, ID2SYM(rb_intern("controller.http_proxy_username"))); controller_http_proxy_password = rb_hash_aref(rb_config_hash, ID2SYM(rb_intern("controller.http_proxy_password"))); controller_http_proxy_password_file = rb_hash_aref(rb_config_hash, ID2SYM(rb_intern("controller.http_proxy_password_file"))); controller_log_dir = rb_hash_aref(rb_config_hash, ID2SYM(rb_intern("controller.log_dir"))); controller_log_level = rb_hash_aref(rb_config_hash, ID2SYM(rb_intern("controller.log_level"))); controller_log_max_num_files = rb_hash_aref(rb_config_hash, ID2SYM(rb_intern("controller.log_max_num_files"))); controller_log_max_file_size = rb_hash_aref(rb_config_hash, ID2SYM(rb_intern("controller.log_max_file_size"))); init_timeout_ms = rb_hash_aref(rb_config_hash, ID2SYM(rb_intern("init_timeout_ms"))); CHECK_TYPE(init_timeout_ms, T_FIXNUM); appd_config_set_app_name(config, StringValueCStr(app_name)); appd_config_set_tier_name(config, StringValueCStr(tier_name)); appd_config_set_node_name(config, StringValueCStr(node_name)); appd_config_set_controller_host(config, StringValueCStr(controller_host)); appd_config_set_controller_port(config, FIX2UINT(controller_port)); appd_config_set_controller_account(config, StringValueCStr(controller_account)); appd_config_set_controller_access_key(config, StringValueCStr(controller_access_key)); appd_config_set_controller_use_ssl(config, RTEST(controller_use_ssl) ? 1 : 0); appd_config_set_logging_log_dir(config, StringValueCStr(controller_log_dir)); if (controller_log_level != Qnil) { appd_config_set_logging_min_level(config, FIX2UINT(controller_log_level)); } if (controller_log_max_num_files != Qnil) { appd_config_set_logging_max_num_files(config, FIX2UINT(controller_log_max_num_files)); } if (controller_log_max_file_size != Qnil) { appd_config_set_logging_max_file_size_bytes(config, FIX2UINT(controller_log_max_file_size)); } appd_config_set_init_timeout_ms(config, FIX2UINT(init_timeout_ms)); if (controller_cert_path != Qnil) { if (TYPE(controller_cert_path) == T_STRING) { LOG(logger, "info", "Setting cert path: %s", StringValueCStr(controller_cert_path)); appd_config_set_controller_certificate_file(config, StringValueCStr(controller_cert_path)); } else { LOG(logger, "warn", "Ignoring cert path, invalid type"); } } if (controller_http_proxy_host != Qnil) { if (TYPE(controller_http_proxy_host) == T_STRING) { LOG(logger, "info", "Setting HTTP proxy host: %s", StringValueCStr(controller_http_proxy_host)); appd_config_set_controller_http_proxy_host(config, StringValueCStr(controller_http_proxy_host)); } else { LOG(logger, "warn", "Ignoring HTTP proxy host, invalid type"); } } if (controller_http_proxy_port != Qnil) { if (TYPE(controller_http_proxy_port) == T_FIXNUM) { LOG(logger, "info", "Setting HTTP proxy port: %i", FIX2UINT(controller_http_proxy_port)); appd_config_set_controller_http_proxy_port(config, FIX2UINT(controller_http_proxy_port)); } else { LOG(logger, "warn", "Ignoring HTTP proxy port, invalid type"); } } if (controller_http_proxy_username != Qnil) { if (TYPE(controller_http_proxy_username) == T_STRING) { LOG(logger, "info", "Setting HTTP proxy username: %s", StringValueCStr(controller_http_proxy_username)); appd_config_set_controller_http_proxy_username(config, StringValueCStr(controller_http_proxy_username)); } else { LOG(logger, "warn", "Ignoring HTTP proxy username, invalid type"); } } if (controller_http_proxy_password != Qnil) { if (TYPE(controller_http_proxy_password) == T_STRING) { LOG(logger, "info", "Setting HTTP proxy password"); appd_config_set_controller_http_proxy_password(config, StringValueCStr(controller_http_proxy_password)); } else { LOG(logger, "warn", "Ignoring HTTP proxy password, invalid type"); } } if (controller_http_proxy_password_file != Qnil) { if (TYPE(controller_http_proxy_password_file) == T_STRING) { LOG(logger, "info", "Setting HTTP proxy password file: %s", StringValueCStr(controller_http_proxy_password_file)); appd_config_set_controller_http_proxy_password_file(config, StringValueCStr(controller_http_proxy_password_file)); } else { LOG(logger, "warn", "Ignoring HTTP proxy password file, invalid type"); } } instrumenter->config = config; if (WITHOUT_GVL(instrumenter_start_nogvl, instrumenter)) { INFO("Initialized AppDynamics SDK"); instrumenter->started = true; return Qtrue; } else { ERROR("Unable to initialize AppDynamics SDK"); return Qfalse; } } // TODO: Revist this since it doesn't do much static VALUE instrumenter_start(VALUE self) { sky_instrumenter_t* instrumenter; My_Struct(instrumenter, sky_instrumenter_t, no_instrumenter_msg); if (instrumenter->started) { rb_raise(rb_eRuntimeError, "%s", existing_instrumenter_msg); } return Qtrue; } static VALUE instrumenter_stop(VALUE self) { sky_instrumenter_t* instrumenter; My_Struct(instrumenter, sky_instrumenter_t, no_instrumenter_msg); if (instrumenter->started) { INFO("Terminating AppDynamics SDK"); sky_deactivate_memprof(); TRACE("appd_sdk_term before"); appd_sdk_term(); instrumenter->started = false; DEBUG("Terminated AppDynamics SDK"); } return Qnil; } int sort_span_duration_desc(const void *a, const void *b) { sky_span_t *span_a = *(sky_span_t **)a; sky_span_t *span_b = *(sky_span_t **)b; long long a_dur = span_a->stop - span_a->start; long long b_dur = span_b->stop - span_b->start; return (int)(b_dur - a_dur); } static VALUE instrumenter_submit_trace(VALUE self, VALUE rb_trace) { sky_trace_t* trace; uint i = 0; Transfer_Struct(trace, rb_trace, sky_trace_t, no_trace_msg); if (sky_have_memprof()) { trace->allocations = sky_consume_allocations(); } TRACE("start: %llu", trace->start); TRACE("endpoint: %s", trace->endpoint); if (sky_have_memprof()) { TRACE("allocations: %" PRIu64, trace->allocations); } TRACE("num_spans: %d", trace->num_spans); TRACE("spans:"); // TODO: Only iterate here if tracing for (i=0; i < trace->num_spans; i++) { sky_span_t *span = trace->spans[i]; TRACE(" %u:", i); TRACE(" is_exitcall: %i", span->is_exitcall); TRACE(" start: %llu", span->start); TRACE(" stop: %llu", span->stop); TRACE(" category: %s", span->category); TRACE(" title: %s", span->title ? span->title : "-"); TRACE(" description: %s", span->description ? span->description : "-"); } if (appd_bt_is_snapshotting(trace->handle)) { sky_span_t *gc_span = NULL; sky_span_t **sql_spans = malloc(trace->num_spans * sizeof(sky_span_t*)); uint num_sql_spans = 0; TRACE("Logging additional snapshot details"); // Set starting URL if (trace->request_url != NULL) { LOG(rb_mAppDynamics, "log_debug", "Setting URL: %s", trace->request_url); appd_bt_set_url(trace->handle, trace->request_url); } if (sky_have_memprof()) { char buffer[20]; // 64-bit number is 19 chars max sprintf(buffer, "%" PRIu64, trace->allocations); appd_bt_add_user_data(trace->handle, "Objects Allocated", buffer); } // Find GC and SQL spans for (i=0; i < trace->num_spans; i++) { sky_span_t *span = trace->spans[i]; if (strcmp(span->category, "db.sql.query") == 0) { sql_spans[num_sql_spans++] = span; } else if (strcmp(span->category, "noise.gc") == 0) { gc_span = span; } } if (gc_span != NULL) { // Log GC details in snapshot char value[25]; // More than enough for duration float duration; duration = (float)(gc_span->stop - gc_span->start) / 10; sprintf(value, "%.2f ms", duration); TRACE("GC Time: %s", value); appd_bt_add_user_data(trace->handle, "GC Time", value); } // Sort SQL spans in reverse duration order qsort(sql_spans, num_sql_spans, sizeof(sky_span_t*), sort_span_duration_desc); // Log details about each SQL statement in snapshot for (i=0; i < num_sql_spans && i < 10; i++) { char key[25]; // Allows for 18 string chars and up to 7 numbers, more than enough char *value = NULL; float duration; int aspfRC; sky_span_t *span = sql_spans[i]; sprintf(key, "Top SQL statement %i", i+1); duration = (float)(span->stop - span->start) / 10; aspfRC = asprintf(&value, "%.2f ms: %s", duration, span->description); if (aspfRC > -1) { TRACE("%s: %s", key, value); appd_bt_add_user_data(trace->handle, key, value); } else { ERROR("asprintf failed; unable to log slow SQL statement"); } free(value); } free(sql_spans); } else { TRACE("Not snapshotting, won't log extra details"); } TRACE("Ending BT; endpoint=%s; handle=%p", trace->endpoint, trace->handle); appd_bt_end(trace->handle); return Qnil; } static VALUE instrumenter_track_desc(VALUE self, VALUE rb_endpoint, VALUE rb_desc) { UNUSED(self); UNUSED(rb_endpoint); UNUSED(rb_desc); return Qtrue; } /* * * class AppDynamics::Trace * */ void trace_mark(sky_trace_t *trace) { uint i; if (trace->spans != NULL) { for (i = 0; i < trace->num_spans; i++) { sky_span_t *span = trace->spans[i]; // This is a Ruby object, so mark it to show that we care about it rb_gc_mark(span->meta); } } } void trace_free(sky_trace_t *trace) { uint i; free(trace->endpoint); free(trace->request_url); for (i = 0; i < trace->num_spans; i++) { sky_span_t *span = trace->spans[i]; free(span->category); free(span->title); free(span->description); // No need to free meta since it is a Ruby object, but we do need to mark it free(span); } free(trace->spans); free(trace); } static VALUE trace_new(VALUE klass, VALUE start, VALUE uuid, VALUE endpoint, VALUE meta) { sky_trace_t *trace = malloc(sizeof(sky_trace_t)); char *uuid_str = NULL; char *endpoint_str = NULL; char *correlation_header = NULL; char *request_url = NULL; char *tmp = NULL; bool ignored; UNUSED(klass); CHECK_NUMERIC(start); CHECK_TYPE(endpoint, T_STRING); if (meta != Qnil) { VALUE rb_correlation_header; VALUE rb_request_url; CHECK_TYPE(meta, T_HASH); // Could/should the uuid be used for the correlation header? rb_correlation_header = rb_hash_aref(meta, ID2SYM(rb_intern("correlation_header"))); if (TYPE(rb_correlation_header) == T_STRING) { correlation_header = StringValueCStr(rb_correlation_header); } rb_request_url = rb_hash_aref(meta, ID2SYM(rb_intern("request_url"))); if (TYPE(rb_request_url) == T_STRING) { tmp = StringValueCStr(rb_request_url); request_url = malloc(sizeof(char) * strlen(tmp) + 1); strcpy(request_url, tmp); } } uuid_str = StringValueCStr(uuid); endpoint_str = StringValueCStr(endpoint); ignored = (strcmp(endpoint_str, IGNORE) == 0); LOG(rb_mAppDynamics, "log_debug", "trace=%s; BT Begin: %s, %s, ignored=%d", uuid_str, endpoint_str, correlation_header, ignored); if (ignored) { trace->handle = NULL; trace->ignored = true; } else { trace->handle = appd_bt_begin(endpoint_str, correlation_header); trace->ignored = false; LOG(rb_mAppDynamics, "log_trace", "trace=%s; BT handle=%p", uuid_str, trace->handle); if (trace->handle == NULL) { LOG(rb_mAppDynamics, "log_warn", "trace=%s; Unable to obtain a BT handle, request will not be tracked.", uuid_str); } } trace->start = NUM2ULL(start); tmp = StringValueCStr(endpoint); trace->endpoint = malloc(sizeof(char) * strlen(tmp) + 1); strcpy(trace->endpoint, tmp); trace->request_url = request_url; // Reset allocations counter if (sky_have_memprof()) { sky_consume_allocations(); } trace->num_spans = 0; trace->spans = NULL; return Data_Wrap_Struct(klass, trace_mark, trace_free, trace); } static VALUE trace_get_started_at(VALUE self) { sky_trace_t* trace; My_Struct(trace, sky_trace_t, no_trace_msg); return ULL2NUM(trace->start); } static VALUE trace_get_endpoint(VALUE self) { sky_trace_t* trace; My_Struct(trace, sky_trace_t, no_trace_msg); return rb_str_new_cstr(trace->endpoint); } // AppDynamics doesn't allow us to set after the fact static VALUE trace_set_endpoint(VALUE self, VALUE endpoint) { return Qnil; } static VALUE trace_set_exception(VALUE self, VALUE exception) { sky_trace_t* trace; VALUE exception_str; char* error_name; bool mark_bt_as_error; My_Struct(trace, sky_trace_t, no_trace_msg); if (trace->ignored) { return Qnil; } exception_str = rb_funcall(exception, rb_intern("to_s"), 0); error_name = StringValueCStr(exception_str); mark_bt_as_error = true; DEBUG("Adding error: %s", error_name); appd_bt_add_error(trace->handle, APPD_LEVEL_ERROR, error_name, mark_bt_as_error); return Qnil; } static VALUE trace_get_uuid(VALUE self) { return Qnil; } static VALUE trace_start_span(VALUE self, VALUE time, VALUE category) { sky_trace_t* trace; sky_span_t* span = malloc(sizeof(sky_span_t)); uint span_id; char* tmp; My_Struct(trace, sky_trace_t, no_trace_msg); if (trace->ignored) { return Qnil; } CHECK_NUMERIC(time); CHECK_TYPE(category, T_STRING); span->is_exitcall = false; span->start = NUM2ULL(time); tmp = StringValueCStr(category); span->category = malloc(sizeof(char) * strlen(tmp) + 1); strcpy(span->category, tmp); span->title = NULL; span->description = NULL; span->meta = Qnil; span_id = trace->num_spans; if (span_id % SPAN_BATCH == 0) { if (span_id >= MAX_SPANS) { rb_raise(rb_eRuntimeError, "Exceeded maximum number of spans (%d)", MAX_SPANS); } TRACE("Growing spans size to %i", span_id+SPAN_BATCH); trace->spans = realloc(trace->spans, (span_id+SPAN_BATCH) * sizeof(sky_span_t*)); } trace->spans[span_id] = span; trace->num_spans++; return UINT2NUM(span_id); } static VALUE trace_stop_span(VALUE self, VALUE span_id, VALUE time) { sky_trace_t* trace; sky_span_t* span; UNUSED(self); My_Struct(trace, sky_trace_t, no_trace_msg); if (trace->ignored) { return Qnil; } CHECK_TYPE(span_id, T_FIXNUM); CHECK_NUMERIC(time); // In theory we should check that the spans are closed in order span = trace->spans[FIX2UINT(span_id)]; span->stop = NUM2ULL(time); if (span->is_exitcall) { char *title; int rc; title = span->title ? span->title : span->category; DEBUG("Setting exitcall title: %s", title); rc = appd_exitcall_set_details(span->handle, title); if (rc) { ERROR("Failed to set exitcall details; %s; err=%i", title, rc); } appd_exitcall_end(span->handle); } return Qnil; } static VALUE trace_span_get_category(VALUE self, VALUE span_id) { sky_trace_t* trace; sky_span_t* span; My_Struct(trace, sky_trace_t, no_trace_msg); if (trace->ignored) { return Qnil; } CHECK_TYPE(span_id, T_FIXNUM); span = trace->spans[FIX2UINT(span_id)]; return rb_str_new_cstr(span->category); } static VALUE trace_span_set_title(VALUE self, VALUE span_id, VALUE title) { sky_trace_t* trace; sky_span_t* span; char *tmp; My_Struct(trace, sky_trace_t, no_trace_msg); if (trace->ignored) { return Qnil; } CHECK_TYPE(span_id, T_FIXNUM); CHECK_TYPE(title, T_STRING); span = trace->spans[FIX2UINT(span_id)]; tmp = StringValueCStr(title); span->title = malloc(sizeof(char) * strlen(tmp) + 1); strcpy(span->title, tmp); return Qnil; } static VALUE trace_span_get_title(VALUE self, VALUE span_id) { sky_trace_t* trace; sky_span_t* span; My_Struct(trace, sky_trace_t, no_trace_msg); if (trace->ignored) { return Qnil; } CHECK_TYPE(span_id, T_FIXNUM); span = trace->spans[FIX2UINT(span_id)]; return span->title ? rb_str_new_cstr(span->title) : Qnil; } static VALUE trace_span_set_description(VALUE self, VALUE span_id, VALUE desc) { sky_trace_t* trace; sky_span_t* span; char *tmp; My_Struct(trace, sky_trace_t, no_trace_msg); if (trace->ignored) { return Qnil; } CHECK_TYPE(span_id, T_FIXNUM); CHECK_TYPE(desc, T_STRING); span = trace->spans[FIX2UINT(span_id)]; tmp = StringValueCStr(desc); span->description = malloc(sizeof(char) * strlen(tmp) + 1); strcpy(span->description, tmp); return Qnil; } static VALUE trace_span_set_meta(VALUE self, VALUE span_id, VALUE meta) { sky_trace_t* trace; sky_span_t* span; My_Struct(trace, sky_trace_t, no_trace_msg); if (trace->ignored) { return Qnil; } CHECK_TYPE(span_id, T_FIXNUM); CHECK_TYPE(meta, T_HASH); span = trace->spans[FIX2UINT(span_id)]; span->meta = meta; return Qnil; } static VALUE trace_span_started(VALUE self, VALUE span_id) { sky_trace_t* trace; sky_span_t* span; VALUE rb_mAppDynamics; VALUE rb_mBackend; VALUE rb_iBackendSub; My_Struct(trace, sky_trace_t, no_trace_msg); if (trace->ignored) { return Qnil; } CHECK_TYPE(span_id, T_FIXNUM); span = trace->spans[FIX2UINT(span_id)]; rb_mAppDynamics = rb_define_module("AppDynamics"); rb_mBackend = rb_define_module_under(rb_mAppDynamics, "Backend"); rb_iBackendSub = rb_funcall(rb_mBackend, rb_intern("build"), 4, rb_str_new_cstr(span->category), span->title ? rb_str_new_cstr(span->title) : Qnil, span->description ? rb_str_new_cstr(span->description) : Qnil, span->meta); if (rb_iBackendSub != Qnil) { VALUE rb_oInstrumenter; sky_instrumenter_t* instrumenter; VALUE rb_backend_name; char* backend_name; bool has_backend; uint i; int rc; DEBUG("Attempting to treat as an exit call"); // TODO: We can streamline this by maintining references in the C-code rb_oInstrumenter = rb_iv_get(self, "@instrumenter"); Get_Struct(instrumenter, rb_oInstrumenter, sky_instrumenter_t, "missing native instrumenter"); // FIXME: Maybe this should include the type too rb_backend_name = rb_funcall(rb_iBackendSub, rb_intern("backend_name"), 0); backend_name = StringValueCStr(rb_backend_name); has_backend = false; for (i = 0; i < instrumenter->num_backends; i++) { if (strcmp(instrumenter->backends[i], backend_name) == 0) { has_backend = true; break; } } if (has_backend) { TRACE("Backend already added: %s", backend_name); } else { int backend_idx; VALUE backend_type; VALUE identifying_properties; uint ipc; DEBUG("Adding backend: %s", backend_name); backend_type = rb_funcall(rb_iBackendSub, rb_intern("backend_type"), 0); appd_backend_declare(StringValueCStr(backend_type), backend_name); identifying_properties = rb_funcall(rb_iBackendSub, rb_intern("identifying_properties_array"), 0); ipc = (int) RARRAY_LEN(identifying_properties); for (i = 0; i < ipc; i += 2) { VALUE rb_key = rb_ary_entry(identifying_properties, i); VALUE rb_val = rb_ary_entry(identifying_properties, i+1); char *key = StringValueCStr(rb_key); char *val = StringValueCStr(rb_val); DEBUG("Identifying: %s - %s\n", key, val); rc = appd_backend_set_identifying_property(backend_name, key, val); if (rc) { ERROR("Unable to set identifying property for backend: %s; key=%s; val=%s; err=%d.", backend_name, key, val, rc); } } backend_idx = instrumenter->num_backends++; strcpy(instrumenter->backends[backend_idx], backend_name); } rc = appd_backend_add(backend_name); if (rc) { ERROR("Unable to add backend: %s; err=%d.", backend_name, rc); } TRACE("Starting exit call; endpoint=%s; backend=%s", trace->endpoint, backend_name); span->is_exitcall = true; span->handle = appd_exitcall_begin(trace->handle, backend_name); TRACE("exit call handle=%p", span->handle); } return Qnil; } static VALUE trace_span_set_exception(VALUE self, VALUE span_id, VALUE exception, VALUE exception_details) { sky_trace_t* trace; sky_span_t* span; VALUE exception_str; char* error_name; bool mark_bt_as_error; My_Struct(trace, sky_trace_t, no_trace_msg); if (trace->ignored) { return Qnil; } span = trace->spans[FIX2UINT(span_id)]; if (span->is_exitcall) { if (exception != Qnil) { exception_str = rb_funcall(exception, rb_intern("to_s"), 0); } else { exception_str = rb_funcall(exception_details, rb_intern("join"), rb_str_new_cstr(", ")); } error_name = StringValueCStr(exception_str); // We'll report the bt error separately mark_bt_as_error = false; DEBUG("Adding span error: %s\n", error_name); appd_exitcall_add_error(span->handle, APPD_LEVEL_ERROR, error_name, mark_bt_as_error); } return Qnil; } static VALUE trace_span_get_correlation_header(VALUE self, VALUE span_id) { sky_trace_t* trace; sky_span_t* span; My_Struct(trace, sky_trace_t, no_trace_msg); if (trace->ignored) { return Qnil; } span = trace->spans[FIX2UINT(span_id)]; if (span->is_exitcall) { return rb_str_new_cstr(appd_exitcall_get_correlation_header(span->handle)); } else { return Qnil; } } static VALUE trace_is_snapshotting(VALUE self) { sky_trace_t* trace; My_Struct(trace, sky_trace_t, no_trace_msg); if (trace->ignored) { return Qfalse; } if (appd_bt_is_snapshotting(trace->handle)) { return Qtrue; } else { return Qtrue; } } static VALUE trace_add_snapshot_details(VALUE self, VALUE key, VALUE value) { sky_trace_t* trace; char* c_key; char* c_value; My_Struct(trace, sky_trace_t, no_trace_msg); if (trace->ignored) { return Qnil; } c_key = StringValueCStr(key); c_value = StringValueCStr(value); appd_bt_add_user_data(trace->handle, c_key, c_value); return Qnil; } static VALUE trace_add_snapshot_url(VALUE self, VALUE url) { sky_trace_t* trace; char* c_url; My_Struct(trace, sky_trace_t, no_trace_msg); if (trace->ignored) { return Qnil; } c_url = StringValueCStr(url); appd_bt_set_url(trace->handle, c_url); return Qnil; } // FIXME: Remove duplication in next two methods static VALUE metrics_define(VALUE self, VALUE name) { char* metric_name; int rc = asprintf(&metric_name, "Custom Metrics|Ruby|%s", StringValueCStr(name)); if (rc > -1) { TRACE("Registering metric: %s", metric_name) appd_custom_metric_add(NULL, metric_name, APPD_TIMEROLLUP_TYPE_AVERAGE, APPD_CLUSTERROLLUP_TYPE_INDIVIDUAL, APPD_HOLEHANDLING_TYPE_RATE_COUNTER); } else { ERROR("Unable to allocate metric_name"); } free(metric_name); return Qnil; } static VALUE metrics_report(VALUE self, VALUE name, VALUE value) { char* metric_name; int rc = asprintf(&metric_name, "Custom Metrics|Ruby|%s", StringValueCStr(name)); if (rc > -1) { unsigned long c_value = NUM2ULONG(value); TRACE("Reporting metric: %s: %lu", metric_name, c_value); appd_custom_metric_report(NULL, metric_name, c_value); } else { ERROR("Unable to allocate metric_name"); } free(metric_name); return Qnil; } #endif void Init_app_dynamics_native() { VALUE rb_cTrace; VALUE rb_cInstrumenter; VALUE rb_cBackgroundMetrics; rb_mAppDynamics = rb_define_module("AppDynamics"); // AppDynamics custom methods rb_define_singleton_method(rb_mAppDynamics, "native?", check_native, 0); rb_define_singleton_method(rb_mAppDynamics, "native_correlation_header", get_correlation_header_name, 0); rb_define_singleton_method(rb_mAppDynamics, "lex_sql", lex_sql, 1); rb_cTrace = rb_const_get(rb_mAppDynamics, rb_intern("Trace")); // Skylight required methods rb_define_singleton_method(rb_cTrace, "native_new", trace_new, 4); rb_define_method(rb_cTrace, "native_get_started_at", trace_get_started_at, 0); rb_define_method(rb_cTrace, "native_get_endpoint", trace_get_endpoint, 0); rb_define_method(rb_cTrace, "native_set_endpoint", trace_set_endpoint, 1); rb_define_method(rb_cTrace, "native_set_exception", trace_set_exception, 1); rb_define_method(rb_cTrace, "native_get_uuid", trace_get_uuid, 0); rb_define_method(rb_cTrace, "native_start_span", trace_start_span, 2); rb_define_method(rb_cTrace, "native_stop_span", trace_stop_span, 2); rb_define_method(rb_cTrace, "native_span_get_category", trace_span_get_category, 1); rb_define_method(rb_cTrace, "native_span_set_title", trace_span_set_title, 2); rb_define_method(rb_cTrace, "native_span_get_title", trace_span_get_title, 1); rb_define_method(rb_cTrace, "native_span_set_description", trace_span_set_description, 2); rb_define_method(rb_cTrace, "native_span_set_meta", trace_span_set_meta, 2); rb_define_method(rb_cTrace, "native_span_started", trace_span_started, 1); rb_define_method(rb_cTrace, "native_span_set_exception", trace_span_set_exception, 3); rb_define_method(rb_cTrace, "native_span_get_correlation_header", trace_span_get_correlation_header, 1); // AppDynamics custom methods rb_define_method(rb_cTrace, "native_is_snapshotting", trace_is_snapshotting, 0); rb_define_method(rb_cTrace, "native_add_snapshot_details", trace_add_snapshot_details, 2); rb_define_method(rb_cTrace, "native_set_snapshot_url", trace_add_snapshot_url, 1); rb_cInstrumenter = rb_const_get(rb_mAppDynamics, rb_intern("Instrumenter")); // Skylight required methods rb_define_singleton_method(rb_cInstrumenter, "native_new", instrumenter_new, 0); rb_define_method(rb_cInstrumenter, "native_start", instrumenter_start, 0); rb_define_method(rb_cInstrumenter, "native_start_sdk", instrumenter_start_sdk, 0); rb_define_method(rb_cInstrumenter, "native_stop", instrumenter_stop, 0); rb_define_method(rb_cInstrumenter, "native_submit_trace", instrumenter_submit_trace, 1); rb_define_method(rb_cInstrumenter, "native_track_desc", instrumenter_track_desc, 2); // AppDynamics custom methods rb_cBackgroundMetrics = rb_const_get(rb_mAppDynamics, rb_intern("BackgroundMetrics")); rb_define_method(rb_cBackgroundMetrics, "native_define_metric", metrics_define, 1); rb_define_method(rb_cBackgroundMetrics, "native_report_metric", metrics_report, 2); }
set_snapshot_url(url)
click to toggle source
Set the URL for the current trace's snapshot. See {Trace#set_snapshot_url} for more details.
# File lib/app_dynamics.rb, line 92 def self.set_snapshot_url(url) return unless instrumenter instrumenter.set_snapshot_url(url) end